#sql #sql-server #tsql
Вопрос:
CREATE TABLE CODES(analysis_date Date, code Varchar(50));
INSERT INTO CODES VALUES('2021-01-01','PROD_OP21_115');
INSERT INTO CODES VALUES('2021-02-06','PROD_TR21_009R');
INSERT INTO CODES VALUES('2021-03-14','PROD_TR21_002');
INSERT INTO CODES VALUES('2021-05-01','PROD_OP21_050R');
INSERT INTO CODES VALUES('2020-06-04','PROD_OP20_450');
Например, у меня есть столбец, в котором хранится строка PROD_OP21_182
. Я разрабатываю запрос, который возвращает все правильно введенные коды (удовлетворяющие всем условиям).
- Это всегда начинается с
PROD_
. - После
PROD_
этого должна быть одна из этих аббревиатур:OP
,UG
илиTR
. - 21 означает две последние цифры года, указанные в другом столбце (дата анализа).
- Последняя
_
и три цифры. - Код может заканчиваться буквой R (это необязательно). Пример:
PROD_OP21_182R
. - Всегда в верхнем регистре.
Это моя попытка:
select CODE
from CODES
where SUBSTRING(CODE, 1, 4) = 'PROD'
and SUBSTRING(CODE, 5, 1) = '_'
and SUBSTRING(CODE, 6, 2) in ('OP','UG','TR')
and RIGHT(YEAR(analysis_date), 2) = SUBSTRING(CODE, 8, 2)
and SUBSTRING(CODE, 10, 1) = '_'
and (
(len(CODE) = 13)
or
(len(CODE) = 14) and CODE like '%R')
Это работает частично. Мне все еще нужно проверить, являются ли три символа SUBSTRING(dispatch_number, 11, 3)
цифрами и выполняется ли правило прописных букв (просто в коде не может быть строчных букв).
Комментарии:
1. Ты просто пишешь запрос? Вы не спрашиваете нас, как ограничить будущий ввод в таблицу?
2. @АннЛ. Предполагается, что запрос вернет все коды, соответствующие структуре.
3. Я бы, наверное, просто связал кучу
LIKE
s-не элегантно, но он выполняет свою работу, имеет предсказуемое поведение индексирования и его легко понять.code LIKE CONCAT('PROD_OP', YEAR(analysis_date) % 100, '_[0-9][0-9][0-9]') COLLATE Latin1_General_CS_AS OR code LIKE CONCAT('PROD_OP', YEAR(analysis_date) % 100, '_[0-9][0-9][0-9]R') COLLATE Latin1_General_CS_AS
и снова дляUG
и.TR
ПовторенноеCOLLATE
можно исключить, если вместо этого вы сделаете его частью определения столбца.4. @Hallah:
ISNUMERIC
это плохая функция, которую почти никогда не следует использовать. Например,'-'
,'.'
и'$'
все строкиISNUMERIC
считаются «числовыми», что большинство здравомыслящих людей не сделали бы.5. Если это вообще возможно, вам следует подумать об исправлении этой таблицы. Это нарушает 1NF, храня несколько фрагментов информации в одном кортеже. Это должны быть столбцы в таблице, а затем код может быть вычисляемым столбцом, если поместить всю эту информацию в нужную строку. Большим преимуществом этого будет то, что вы сможете легко создавать ограничения для каждого элемента, чтобы их можно было проверить.
Ответ №1:
Прежде всего, похвала за публикацию образцов данных и структуры таблиц, чтобы любому было легко помочь. Я бы хотел, чтобы все могли это сделать. Как я уже упоминал в своем комментарии, структура таблицы, которую вы здесь имеете, объясняет, почему это так сложно. Вы храните кучу информации в одном кортеже, который нарушает 1NF. Однако мы не всегда можем это исправить и вынуждены запрашивать информацию, которую нам дают.
Это один из способов решить эту проблему. Вместо подстроки я использую parsename. Это немного больше кода, но я нахожу использование подстроки действительно сложным, потому что, когда что-то меняется в логике, вам приходится возиться с каждым экземпляром подстроки, чтобы правильно получить новые значения.
select *
from CODES
where parsename(replace(code, '_', '.'), 3) COLLATE Latin1_General_CS_AS = 'PROD'
AND
(
left(parsename(replace(code, '_', '.'), 2), 2) in ('OP','UG','TR')
OR
try_parse(right(parsename(replace(code, '_', '.'), 2), 2) as int) = datepart(year, analysis_date)
)
AND
(
parsename(replace(code, '_', '.'), 1) like '[0-9][0-9][0-9]'
OR
parsename(replace(code, '_', '.'), 1) COLLATE Latin1_General_CS_AS like '[0-9][0-9][0-9][R]'
)
Комментарии:
1. Все должно идти в кортеже, так как каждый код представляет собой буровую скважину в горнодобывающей компании. PROD означает производственную дрель; OP, UG и т. Д. это места расположения жил (открытый карьер, подземный и т.д.). В любом случае, причина, по которой все должно идти в одном кортеже (в дополнение к политике компании), заключается в том, что это экспортируется в программу географического моделирования, и геологи хотят, чтобы каждое имя было как можно более репрезентативным.
Ответ №2:
Этот код, по-видимому, соответствует требованиям. Все сравнения букв [A-Z] выполняются с указанием параметров сортировки Latin1_General_CS_AS, учитывающих регистр.
select *
from #codes
where substring(code, 1, 5) COLLATE Latin1_General_CS_AS='PROD_'
and SUBSTRING(code, 6, 2) COLLATE Latin1_General_CS_AS in ('OP','UG','TR')
and RIGHT(YEAR(analysis_date), 2) = SUBSTRING(CODE, 8, 2)
and SUBSTRING(code, 10, 4) like '_[0-9][0-9][0-9]'
and (len(CODE) = 13
or
(len(CODE) = 14 and right(CODE, 1) COLLATE Latin1_General_CS_AS='R'));
analysis_date code
2021-01-01 PROD_OP21_115
2021-02-06 PROD_TR21_009R
2021-03-14 PROD_TR21_002
2021-05-01 PROD_OP21_050R
2020-06-04 PROD_OP20_450
Ответ №3:
Вы можете использовать параметры сортировки с учетом регистра, например
..
and SUBSTRING(CODE, 6, 2) COLLATE Latin1_General_CS_AS in ('OP','UG','TR')
..
и, КАК в случае с простым регулярным выражением, поддерживаемым SQL Server
..
and SUBSTRING(CODE, 11, 3) like '[0-9][0-9][0-9]'
..
Комментарии:
1. так как есть 3 числа, не должно быть похоже [0-9][0-9][0-9]?
2. @Алла, определенно. Спасибо.