#sql #sql-server #sqlite
#sql #sql-сервер #sqlite
Вопрос:
Прежде всего, я пытаюсь добиться следующего:
Сначала я использовал MS SQL, чтобы выяснить, как я могу выровнять теги вместе.
Вот схема, если вы хотите воссоздать ненормализованную таблицу.
CREATE TABLE unnormalized(
vendor_tag varchar(200),
vendor_tag_name varchar(200),
vendor_id int
);
INSERT INTO unnormalized
VALUES
('5,8,30,24','Burgers,Desserts,Fries,Salads',1),
('5','Burgers',2),
('8,42','Desserts,Mexican',3),
('1,5,30,16','American,Burgers,Fries,Sandwiches',4),
('1,5,30,16','American,Burgers,Fries,Sandwiches',5);
Вот код для нормализованной таблицы
SELECT
--*
DISTINCT CAST(tag_id AS INT) as tag_id ,tag_name
FROM unnormalized
CROSS APPLY
(
(SELECT
value as tag_id,
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rn
FROM STRING_SPLIT(vendor_tag,',')
) a1
INNER JOIN
(SELECT
value as tag_name,
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rn
FROM STRING_SPLIT(vendor_tag_name,',')
) a2
ON a1.rn = a2.rn
)
ORDER BY tag_id
Теперь я пытаюсь переписать этот код с помощью SQLite. Однако есть несколько отличий, таких как ‘CROSS APPLY’ и ‘STRING_SPLIT’, отсутствующих в SQLite. Я огляделся и обнаружил, что ПЕРЕКРЕСТНОЕ ПРИМЕНЕНИЕ, возможно, похоже на «ПЕРЕКРЕСТНОЕ СОЕДИНЕНИЕ» в SQLite и, возможно, использует что-то подобное, чтобы отделить строку от первой найденной запятой??
WITH split(vendor_id, vendor_tag, str) AS (
SELECT vendor_id, '', vendor_tag||',' FROM unnormalized
UNION ALL SELECT vendor_id,
substr(str, 0, instr(str, ',')),
substr(str, instr(str, ',') 1)
FROM split
WHERE str
)
SELECT vendor_id, vendor_tag
FROM split
WHERE vendor_tag
ORDER BY vendor_id;
Комментарии:
1. Исправьте свою модель данных! Не храните несколько значений в одном столбце! Не храните числа в виде строк!
2. Эта таблица не просто «ненормализована», она нарушает самое основное правило проектирования — каждая ячейка должна содержать атомарное значение. Если бы вы использовали правильную схему, у вас не было бы никаких проблем. Даже в базах данных, содержащих массивы, нет никакой связи между элементами разных массивов
3. Что касается SQLite, вы вообще ничего не получаете, разделяя значения в SQL. SQLite — это встроенная база данных, что означает, что движок размещается и запускается вашим приложением, используя оперативную память вашего приложения. Намного быстрее разделить строки на языке вашего клиентского приложения, чем пытаться сделать то же самое в SQLite
4. @Panagiotis Kanavos Таблица представляет собой данные из файла csv, который я нашел в kaggle и выполнял некоторые упражнения для заполнения данных в базу данных
5. Почему вы использовали такую схему? Какую проблему вы пытались исправить? Не скорость или масштабируемость — эта схема чрезвычайно медленная и вообще не масштабируется. Каждый запрос должен сканировать всю таблицу и не может использовать какие-либо индексы. Пробел? Вероятно, это занимает больше места, чем правильная таблица с целочисленными идентификаторами, даже для небольшого числа поставщиков. Если у вас много поставщиков, вы можете использовать сжатие таблиц в SQL Server
Ответ №1:
В SQLite вы можете сделать это с помощью рекурсивного CTE:
WITH cte AS (
SELECT
vendor_tag,
vendor_tag_name,
SUBSTR(vendor_tag, 1, INSTR(vendor_tag || ',', ',') - 1) col1,
SUBSTR(vendor_tag_name, 1, INSTR(vendor_tag_name || ',', ',') - 1) col2
FROM unnormalized
UNION ALL
SELECT
SUBSTR(vendor_tag, LENGTH(col1) 2),
SUBSTR(vendor_tag_name, LENGTH(col2) 2),
SUBSTR(SUBSTR(vendor_tag, LENGTH(col1) 2), 1, INSTR(SUBSTR(vendor_tag, LENGTH(col1) 2) || ',', ',') - 1),
SUBSTR(SUBSTR(vendor_tag_name, LENGTH(col2) 2), 1, INSTR(SUBSTR(vendor_tag_name, LENGTH(col2) 2) || ',', ',') - 1)
FROM cte
WHERE LENGTH(vendor_tag) AND LENGTH(vendor_tag_name)
)
SELECT DISTINCT col1 vendor_tag, col2 vendor_tag_name
FROM cte
WHERE NOT (INSTR(col1, ',') OR INSTR(col2, ',')) AND (LENGTH(col1) AND LENGTH(col2))
ORDER BY vendor_tag 0
Посмотрите демонстрацию.
Результаты:
> vendor_tag | vendor_tag_name
> :--------- | :--------------
> 1 | American
> 5 | Burgers
> 8 | Desserts
> 16 | Sandwiches
> 24 | Salads
> 30 | Fries
> 42 | Mexican
Комментарии:
1. Вау, это работает, спасибо!! На данный момент я мало изучал рекурсивный CTE, и мне немного сложно читать код. Но я изучу код немного дальше!
2.
SUBSTR(vendor_tag, LENGTH(col1) 2), SUBSTR(vendor_tag_name, LENGTH(col2) 2), SUBSTR(SUBSTR(vendor_tag, LENGTH(col1) 2), 1, INSTR(SUBSTR(vendor_tag, LENGTH(col1) 2) || ',', ',') - 1), SUBSTR(SUBSTR(vendor_tag_name, LENGTH(col2) 2), 1, INSTR(SUBSTR(vendor_tag_name, LENGTH(col2) 2) || ',', ',') - 1)
Не могли бы вы дать немного больше информации об этой части кода?3. @Q.T Рекурсивный CTE на самом деле представляет собой цикл (что-то вроде цикла while в других языках программирования). На каждой итерации я получаю часть строки до первой
,
, а на следующей итерации я получаю часть оставшейся строки до следующей,
и так далее. Вот почему я использую строковые функции, такие как INSTR(), SUBSTR() .4. Привет, еще один вопрос. Как я могу использовать этот код, чтобы вставить его в эту таблицу?
CREATE TABLE IF NOT EXISTS tag ( tag_id INTEGER PRIMARY KEY, tag_name TEXT, );
5. @Q.T проверьте это: dbfiddle.uk /…