SQL: Как запросить элементы из одной таблицы, которые не находятся в другой, когда синтаксис не совпадает? - PullRequest
3 голосов
/ 07 января 2011

У меня вопрос, потому что я очень плохо разбираюсь в SQL.Я понимаю основные функции, но когда
становится немного сложнее, я полностью теряюсь.

вот что у меня есть:
таблицы: tA, tB
столбцы: tA: refAtB: refB
в основном refA и refB представляют одно и то же (некоторый идентификатор формы, такой как xxx-xxx-xxx), но к refB может добавляться информация (например, xxx-xxx-xxx_Zxxx или xxx-xxx-xxx Zxxx)

вот что я умею делать:
запрашивать элементы, которые есть в таблице, но не в другой (когда они абсолютно одинаковые)

select refA
from tA
where not exists (select *
from tB
where tB.refB = tA.refA
)

Что яхочу сделать:
Я хочу запрос, который будет перечислять элементы из refA, которые не в refB.НО, проблема в том, что если я выполню «простой» запрос с НЕ СУЩЕСТВУЮЩИМ, как я только что показал, он вернет все из-за добавления.поэтому я подумал об использовании некоторого синтаксиса, подобного этому:

SELECT refA
FROM tA
WHERE NOT EXISTS (SELECT * 
FROM tB
WHERE tB.refB LIKE CONCAT(tA.refA,'%'))

, но ... конечно, это не работает.

Может кто-нибудь показать мне, как это должно быть сделано, а такжеобъясните, как это работает, чтобы я мог учиться?
Заранее спасибо!

edit: дополнительная информация
Я не могу использовать left () или что-то подобное, потому чтоформат ref похож, но не всегда одинаков (различается по количеству символов).
Единственный способ определить конец идентификатора перед добавлением - это наличие пробела или подчеркивания.

edit 2: образец данных, вызывающий проблемы (MON, 10 января)

Вот некоторые фактические данные из таблиц, из-за которых большинство ответов, которые люди дали здесь, упускают некоторые результаты:/

в тА:
B20-60-04-6A-1
B20-60-04-6A-11
B20-60-04-6A-12
B20-60-04-6A-13

в тБ:
B20-60-04-6A-11_XX
B20-60-04-6A-12_XX
B20-60-04-6A-13_XX

Проблема с mid (), left () и т. Д. Заключается в том, что еслимы проверяем "B20-60-04-6A-1" (14 символов) против 14 первых символов, он вернет 3 положительных значения, хотя на самом деле его нет в ТБ ...

, так как можномы продолжаем?

Примеры шаблонов данных в ТА следующие:
(X, XYZ: символы. X: буквенно-цифровой)
Xxx-xx-xx-x
Xxx-xx-xx-xx
Ххх-хх-хх-хх-хх
Ххх-хх-хх-хх-хх-х
и т. Д.

примеры шаблонов данных в ТБ:
Ххх-хх-xx-xx-xx-XYZ-xx Z xxx_XX
Xxx-xx-xx-xx-xx-XYZZxxx_XX
Xxx-xx-xx-xx-xx Z xxx_XX

XYZ всегда одинаковы3 персонажа.Когда у нас нет XYZ, всегда есть пробел или подчеркивание.

поэтому строка данных, которую мы сравниваем, должна быть обрезана в соответствии с этим:
- от начала до строки -XYZ
- или, если в строке нет -XYZ, от начала к первой ""или" _ "

Я бы написал молниеносно в VBA, но в SQL ... хорошо, я попробую, но я действительно плох в этом: D

Ответы [ 6 ]

2 голосов
/ 07 января 2011

Итак, во-первых, вам нужна функция, которая изменит refB, чтобы не иметь добавленной информации, чтобы ее можно было правильно сравнить с refA.Будет несколько подходов, но что-то вроде этого должно работать:

Left(tb.RefB, InStr(Replace(tb.RefB+"_", " ", "_"), "_") -1)

, который преобразует любой refB, такой как "123-456 123 EXTRA STUFF" или "123-456_123_EXTRA_STUFF" в "123-456".Этот результат должен быть в порядке, если сравнивать непосредственно с refA.

РЕДАКТИРОВАТЬ: краткое объяснение выражения выше.Я делаю следующее:

  1. Добавление символа подчеркивания в конец refB, чтобы всегда было хотя бы одно подчеркивание (это работает в случае, когда refB совпадает с refA, например "123"становится" 123 _ ")
  2. Замена всех пробелов в refB символами подчеркивания (функция Replace).Теперь мы знаем, что разделитель всегда является подчеркиванием, и мы также знаем из шага 1, что будет хотя бы одно подчеркивание.
  3. Поиск местоположения первого подчеркивания (функция InStr).Это позиция, в которой refB разделяется между refA и дополнительными элементами.
  4. Схватывание всех символов между началом строки и первым подчеркиванием, то есть частью перед разделителем.

Итак, это дает вам что-то вроде этого:

select refA
from tA
where not exists (select *
from tB
where Left(tb.RefB, InStr(Replace(tb.RefB+"_", " ", "_"), "_") -1) = tA.refA
)

Я бы использовал этот подход, а не сравнивал с подстановочными знаками или обрезал refB, чтобы соответствовать длине refA, из-за этого сценария:

refA
====
123
123-456
123-456-789

refB
====
123-456-789_This_is_a_test

В этом случае усечение или сопоставление подстановочного знака refA с refB приведет к успеху для всех refAs, поскольку «123 *», «123-456 *» и «123-456-789 *» all соответствует "123-456-789_This_is_a_test".

1 голос
/ 07 января 2011

Я бы изменил схему. Ваша вторая таблица должна иметь два столбца, один из которых содержит первую часть идентификатора, а другой содержит вторую; если первым был первичный ключ столбца, просто создайте уникальный многостолбцовый индекс и запретите значения NULL.

Вы также можете добавить ограничение внешнего ключа и / или оптимизировать сравнения, введя суррогатный ключ в первую таблицу и сославшись на него из второй.

Если у вас нет индекса в подстроке, которую вы пытаетесь сопоставить, у вас будет полное сканирование для каждого искомого значения, это ужасно дорого.

1 голос
/ 07 января 2011

Я думаю, что ваше предложение будет работать в несколько ином формате, обычно подстановочным знаком в Access является *, если вы не установили режим ANSI 92, однако вы можете использовать ALIKE с% в «обычном» режиме.

РЕДАКТИРОВАТЬ: РАЗНАЯ ИДЕЯ

SELECT tA.refA
FROM tA
WHERE (((tA.refA) 
   Not In (SELECT Mid(tb.RefB,1,Len(ta.RefA)) FROM tb)));
1 голос
/ 07 января 2011

То есть, вы хотите все из A, где не в B, но где совпадает только начало id B?

select refA
from tA
left outer join tB 
    on tA.refA = left( tB.refB, len(tA.refA)) --trim B's id to the length of A's
where tB.refB is null
1 голос
/ 07 января 2011

Может быть, использовать функцию left(), если она существует в access?Например:

SELECT refA
FROM tA
WHERE NOT EXISTS (SELECT * 
FROM tB
WHERE Left(tB.refB, Len(tA.refA)) = tA.refA)

Если, как вы сказали, вам нужно искать пробел или подчеркивание в refA, вы можете использовать это:

0 голосов
/ 07 января 2011

Это правильный синтаксис, близкий к синтаксису, который вы хотите написать:

SELECT refA 
  FROM tA
 WHERE NOT EXISTS (
                   SELECT *
                     FROM tB
                    WHERE tB.refB ALIKE tA.refA & '%'
                  );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...