t-sql "LIKE" и сопоставление с образцом - PullRequest
2 голосов
/ 24 августа 2011

Я обнаружил небольшое раздражение, которое мне было интересно, как обойти ...

В упрощенном примере, скажем, мне нужно вернуть "TEST B-19" и "TEST B-20"

У меня есть предложение where, которое выглядит следующим образом:

where [Name] LIKE 'TEST B-[12][90]'

и это работает ... если нет значения "TEST B-10" или "TEST-B29", которое мне не нужно.

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

Я пытался:

where [Name] LIKE 'TEST B-[19-20]'

но, конечно, это не работает, потому что он ищет отдельные символы ...

Мысли? Опять же, это очень простой пример, я бы искал способы получения диапазонов от 16 до 32 или от 234 до 459 без захвата всех дополнительных значений, которые могут быть созданы.

ИЗМЕНЕНО для включения примеров испытаний ...

В этом поле вы можете увидеть «TEXAS 22», «THX 99-20-110-B6», «E-19», «SOUTHERN B» или «122 FLOWERS». Представление цифр является обычным, но не постоянным правилом, и нет абсолютно никаких общих шаблонов для гипсов, цифр, символов, порядка и т. Д.

Ответы [ 4 ]

2 голосов
/ 24 августа 2011

Я бы разделил столбец Name на текстовые части и числовые части, и преобразовал бы числовые части в целое число, а затем проверил, был ли он между значениями.Примерно так:

where cast(substring([Name], 7, 2) as integer) between 19 and 20

И, конечно, если возможная структура [Имя] гораздо сложнее, вам придется вычислять значения для 7 и 2, а не жестко их кодировать ....

РЕДАКТИРОВАТЬ: Если вы хотите сначала отфильтровать те, которые не соответствуют шаблону, сделайте следующее:

where [Name] LIKE '%TEST B-__%'
and cast(substring([Name], CHARINDEX('TEST B-', [Name]) + LEN('TEST B-'), 2) as integer) between 19 and 20

Возможно, быстрее использовать CHARINDEX вместо LIKE ввторая верхняя строка, особенно если вы поместили индекс в вычисляемое значение, но ... это только оптимизация ...:)

РЕДАКТИРОВАТЬ: Протестировал процедуру.Учитывая следующие данные:

jajajajajajajTEST B-100
jajajajajajajTEST B-85
jajajajjTEST B-100
jajjajajTEST B-100
jajajajajajajTEST B-00
jajajajaTEST B-100
jajajajajajajEST B-99
jajajajajajajTEST B-100
jajajajajajajTEST B-19
jajajajjTEST B-100
jajjajajTEST B-120
jajajajajajajTEST B-00
jajajajaTEST B-150
jajajajajajajEST B-20
TEST B-20asdfh asdfkh

Запрос возвращает следующие строки:

jajajajajajajTEST B-19
TEST B-20asdfh asdfkh
1 голос
/ 24 августа 2011

Подстановочные знаки или нет, вам все равно придется редактировать запрос каждый раз, когда вы хотите изменить определение диапазона. Если вы всегда имеете дело с диапазоном (а он не всегда один и тот же), вы можете использовать параметры. Например:

примечание: по какой-то причине (это также случалось во многих других сообщениях), когда я пытаюсь опубликовать код, начинающийся с 'Declare', SO зависает и время ожидания. Я уже сообщал об этом на мета, но никто не мог воспроизвести это (включая меня). Здесь это происходит снова, поэтому я снял букву «D», и теперь она работает. Я вернусь завтра, и это позволит мне снова поставить букву «D».

DECLARE @min varchar(5)
DECLARE @max varchar(5)

SET @min = 'B-19'
SET @max = 'B-20'

SELECT
   ...
WHERE NAME BETWEEN @min AND @max

Вам следует избегать форматирования [ИМЯ], как предлагали другие (используя для этого функцию) - таким образом, для вашего поиска может быть полезен индекс по нему.

В любом случае - вы можете пересмотреть структуру таблицы. Похоже, «ТЕСТ B-19» является составным (ненормализованным) значением категории («ТЕСТ») + подкатегория («В») + экземпляр («19»). Поместите его в таблицу поиска с 4 столбцами (id является первым), а затем объедините его по id в любом запросе, необходимом для вывода составного значения. Это сделает поиск и индексацию намного проще и быстрее.

1 голос
/ 24 августа 2011

При отсутствии тестовых данных я генерировал свои собственные.Я просто удалил префикс Test B-, преобразовал его в int и сделал Between

With Numerals As
(
    Select top 100 row_number() over (order by name)  TestNumeral
    from sys.columns
),
TestNumbers AS
(
    Select 'TEST B-' + Convert (VarChar, TestNumeral) TestNumber
    From Numerals
)
Select * 
From TestNumbers
Where Cast (Replace (TestNumber, 'TEST B-', '') as Integer) between 1 and 16

Это дало мне

TestNumber
-------------------------------------
TEST B-1
TEST B-2
TEST B-3
TEST B-4
TEST B-5
TEST B-6
TEST B-7
TEST B-8
TEST B-9
TEST B-10
TEST B-11
TEST B-12
TEST B-13
TEST B-14
TEST B-15
TEST B-16

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

Теперь, с другой стороны, если ваши тестовые номера имеют формат TEST-Space-TestType-Hyphen-TestNumber, вы можете использовать PatIndex и SubString

With Numerals As
(
    Select top 100 row_number() over (order by name)  TestNumeral
    from sys.columns
),
TestNumbers AS
(
    Select 'TEST B-' + Convert (VarChar, TestNumeral) TestNumber
    From Numerals
    Where TestNumeral Between 10 and 19
    UNION
    Select 'TEST A-' + Convert (VarChar, TestNumeral) TestNumber
    From Numerals
    Where TestNumeral Between 20 and 29
)
Select *
From TestNumbers
Where Cast (SubString (TestNumber, PATINDEX ('%-%', TestNumber)+1, Len (TestNumber) - PATINDEX ('%-%', TestNumber)) as Integer) between 16 and 26

Это должно привести к следующему

TestNumber
-------------------------------------
TEST A-20
TEST A-21
TEST A-22
TEST A-23
TEST A-24
TEST A-25
TEST A-26
TEST B-16
TEST B-17
TEST B-18
TEST B-19

Кажется, у всех ваших примеров есть тестовые номера в конце.Так что, если вы можете создать таблицу шаблонов, а затем ПРИСОЕДИНИТЬСЯ с помощью оператора LIKE, вы сможете заставить его работать.Вот пример:

;
With TestNumbers As
(
      select 'E-1' TestNumber
union select 'E-2'
union select 'E-3'
union select 'E-4'
union select 'E-5'
union select 'E-6'
union select 'E-7'
union select 'SOUTHERN B1'
union select 'SOUTHERN B2'
union select 'SOUTHERN B3'
union select 'SOUTHERN B4'
union select 'SOUTHERN B5'
union select 'SOUTHERN B6'
union select 'SOUTHERN B7'
union select 'Southern CC'
union select 'Southern DD'
union select 'Southern EE'
union select 'TEST B-1'
union select 'TEST B-2'
union select 'TEST B-3'
union select 'TEST B-4'
union select 'TEST B-5'
union select 'TEST B-6'
union select 'TEST B-7'
union select 'TEXAS 1'
union select 'TEXAS 2'
union select 'TEXAS 3'
union select 'TEXAS 4'
union select 'TEXAS 5'
union select 'TEXAS 6'
union select 'TEXAS 7'
union select 'THX 99-20-110-B1'
union select 'THX 99-20-110-B2'
union select 'THX 99-20-110-B3'
union select 'THX 99-20-110-B4'
union select 'THX 99-20-110-B5'
union select 'THX 99-20-110-B6'
union select 'THX 99-20-110-B7'
union select 'Southern AA'
union select 'Southern CC'
union select 'Southern DD'
union select 'Southern EE'
),
Prefixes as
(
    Select 'TEXAS ' TestPrefix
    Union Select 'THX 99-20-110-B'
    Union Select 'E-'
    Union Select 'SOUTHERN B'
    Union Select 'TEST B-'
)
Select TN.TestNumber
From TestNumbers TN, Prefixes P
Where 1=1 
And TN.TestNumber Like '%' + P.TestPrefix + '%'
And Cast (REPLACE (Tn.TestNumber, p.TestPrefix, '') AS INTEGER) between 4 and 6

Это даст вам

TestNumber
----------------
E-4
E-5
E-6
SOUTHERN B4
SOUTHERN B5
SOUTHERN B6
TEST B-4
TEST B-5
TEST B-6
TEXAS 4
TEXAS 5
TEXAS 6
THX 99-20-110-B4
THX 99-20-110-B5
THX 99-20-110-B6

(15 row(s) affected)
0 голосов
/ 24 августа 2011

Допустимо ли:

WHERE [Name] IN ( 'TEST B-19', 'TEST B-20' )

Список значений может быть получен из подзапроса, например:

WHERE [Name] IN ( SELECT [Name] FROM Elsewhere WHERE ... )
...