Удаление дубликатов товаров из левого объединения с помощью самостоятельного объединения

Для sqlite я хотел бы найти способ удалить дубликаты из этого результата (см. эту суть или эту демонстрацию sqlime.org)

Некоторые функции доступны для всех продуктов, а некоторые — только для определенных продуктов.

  • Результат выше возвращает то, что я хочу - все продукты со всеми функциями и, если доступен, текст функции на немецком и английском языках.
  • Результат содержит неправильные строки, например, вторая строка 2024 B12XBE A1C B1 B1 hardened steal UT gehärteter Stahl оба Feature_text должны начинаться с UT

Но он содержит повторяющиеся строки.

Вопрос

  • Как лучше всего избежать дублирования строк?
  • Если возможно: я хотел бы понять основную логику (декартово произведение или подобное).

демонстрация SQL

Этот sql можно использовать в качестве образца

DROP TABLE IF EXISTS product_features;
DROP TABLE IF EXISTS feature_text;
CREATE TABLE product_features (
    year INTEGER, -- (4) 
    product TEXT, -- (6) 
    feature TEXT -- (3)      
);
CREATE TABLE feature_text (
    year INTEGER, -- (4)    
    product_group TEXT, 
     -- (2) usually matches the first two digits of product 
     -- but is sometimes empty / NULL or blank ' ' 
    feature TEXT, -- (3)
    language INTEGER, -- (2) 
    description1 TEXT -- up to 40
);

Вставьте несколько записей

INSERT INTO product_features (year, product, feature) VALUES 
    (2024, 'B12XBE', 'A1C'), (2024, 'B12XBE', '7B0'),
    (2024, 'B12XBE', 'DUL'), (2024, 'UTB2EF', 'A1C'),
    (2024, 'UTB2EF', '7B0'), (2024, 'UTB2EF', 'DUL'),
    (2024, 'X9Y8Z7', 'DUH'), (2024, 'X9Y8Z7', '7B0');
                    
INSERT INTO feature_text
 (year, product_group, feature, language, description1) 
VALUES 
  (2024, 'B1', 'A1C', 1, 'B1 hardened steel'),
  (2024, 'B1', '7B0', 1, 'B1 diamond tip'),
  (2024, 'B1', 'DSP', 1, 'display 1 inch'),
  (2024, 'B1', 'DSP', 1, 'Anzeige 1,5 cm'),  
  (2024, 'UT', 'A1C', 1, 'UT hardened steel'),
  (2024, 'UT', '7B0', 1, 'UT diamond tip'),
  (2024, 'UT', 'DSP', 1, 'display 1,5 inch'),
  (2024, 'UT', 'DSP', 1, 'Anzeige 2,25 cm'),
  (2024, ' ', 'DUL', 1, '10mm for light duty'),
  (2024, 'X9', '7B0', 1, 'X9 diamond tip'),
  (2024, ' ', 'DUH', 1, '13mm for heavy duty'),
  (2024, 'B1', 'A1C', 2, 'B1 gehärteter Stahl'),
  (2024, 'B1', '7B0', 2, 'B1 Diamant Spitze'),
  (2024, 'UT', 'A1C', 2, 'UT gehärteter Stahl'),
  (2024, 'UT', '7B0', 2, 'UT Diamant Spitze'),
  (2024, ' ', 'DUL', 2, '10mm für leichte Aufgaben'),
  (2024, 'X9', '7B0', 2, 'X9 Diamant Spitze'),
  (2024, ' ', 'DUH', 2, '13mm für schwere Aufgaben'),
  (2024, NULL, 'DSP', 1, 'display'),
  (2024, NULL, 'DSP', 1, 'Anzeige');

Мой текущий подход — объединить таблицы и поместить каждый язык в отдельный столбец.

WITH prft_en as (
SELECT 
pf.year, pf.product, pf.feature, 
COALESCE(ft1.product_group, ft2.product_group) AS product_group,
COALESCE(ft1.description1, ft2.description1) AS feature_text_en,
COALESCE(ft1.language, ft2.language) AS language_en
  FROM product_features pf
    LEFT JOIN feature_text  ft1
                 ON pf.year = ft1.year 
                 AND pf.feature = ft1.feature
                 AND substr(pf.product, 1,2) = ft1.product_group
    LEFT JOIN feature_text ft2
                 ON pf.year = ft2.year 
                 AND pf.feature = ft2.feature
                 AND ft2.product_group is ' ' OR ft2.product_group is NULL  
WHERE ft1.language = 1 OR ft2.language = 1                 
), prft_de AS (
SELECT 
pf.year, pf.product, pf.feature, 
COALESCE(ft1.product_group, ft2.product_group) AS product_group,
COALESCE(ft1.description1, ft2.description1) AS feature_text_de,
COALESCE(ft1.language, ft2.language) AS language_de
  FROM product_features pf
    LEFT JOIN feature_text  ft1
                 ON pf.year = ft1.year 
                 AND pf.feature = ft1.feature
                 AND substr(pf.product, 1,2) = ft1.product_group
    LEFT JOIN feature_text ft2
                 ON pf.year = ft2.year 
                 AND pf.feature = ft2.feature
                 AND ft2.product_group is ' ' OR ft2.product_group is NULL  
WHERE ft1.language = 2 OR ft2.language = 2
)

Запрос для cte, созданный выше

SELECT prft_en.year, prft_en.product, 
       prft_en.feature, prft_en.product_group, 
       prft_en.feature_text_en, prft_de.feature_text_de 
FROM prft_en 
     LEFT JOIN prft_de
     ON prft_en.year = prft_de.year
     AND prft_en.feature = prft_de.feature
     AND SUBSTR(prft_en.product, 1,2) = prft_en.product_group

В выводимых вами результатах нет повторяющихся строк, каждая строка отличается хотя бы в одном столбце. Можете ли вы более четко описать, что вы имеете в виду? Также добавьте, какой результат вы хотите. И, пожалуйста, не добавляйте изображения текста (или таблиц), добавляйте текст (таблицу) напрямую.

cafce25 26.08.2024 13:36

не по теме: «украсть» — это глагол, который по-немецки означает «stehlen», вы, вероятно, имели в виду «сталь» ≙ «Stahl»

cafce25 26.08.2024 13:40

@ cafce25 спасибо, что указали на это - каким-то образом в запросе есть еще одна ошибка. 2-й строки в результате быть не должно. Я обновил свой вопрос.

surfmuggle 26.08.2024 13:53
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
3
68
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Я думаю, что все эти CTE на самом деле не то, что вам нужно. Я думаю, вам нужна одна строка для каждого продукта, поэтому начните с этого.

select
    f.*,
    substring(f.product, 1, 2) as product_group
from product_features f

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

    (
        select description1
        from feature_text t
        where f.year = t.year
            and (product_group = t.product_group or t.product_group = ' ' or t.product_group is null)
            and f.feature = t.feature
            and language = 1
    ) as feature_text_en

и для немецкого:

    (
        select description1
        from feature_text t
        where f.year = t.year
            and (product_group = t.product_group or t.product_group = ' ' or t.product_group is null)
            and f.feature = t.feature
            and language = 2
    ) as feature_text_de

Вот оба вместе на sqlime и выдают следующую таблицу результатов:

год продукт особенность группа_продукта Feature_text_en Feature_text_de 2024 год B12XBE А1С Б1 B1 закаленная сталь Комплект поставки B1 Stahl 2024 год B12XBE 7B0 Б1 Алмазный наконечник B1 B1 Диамант Шпитце 2024 год B12XBE ДУЛ Б1 10 мм для легких условий эксплуатации 10 мм для облегчения работы 2024 год UTB2EF А1С ЮТ UT закаленная сталь Специалист UT Stahl 2024 год UTB2EF 7B0 ЮТ UT алмазный наконечник ЮТ ​​Диамант Шпитце 2024 год UTB2EF ДУЛ ЮТ 10 мм для легких условий эксплуатации 10 мм для облегчения работы 2024 год X9Y8Z7 ДУХ Х9 13 мм для тяжелых условий эксплуатации 13 мм для швера Aufgaben 2024 год X9Y8Z7 7B0 Х9 алмазный наконечник X9 X9 Диамант Шпитце

Спасибо, это решает проблему. Функция substring является псевдонимом substr и недоступна в старых версиях sqlite.

surfmuggle 27.08.2024 13:38

Полагаю, все, что вам нужно, это ЛЕВОЕ соединение по сложному условию. Так:

select 
    product_features.year,
    product_features.product,
    product_features.feature,
    substr(product_features.product, 1, 2) AS product_group,
    text_en.description1 AS en,
    text_de.description1 AS de
FROM product_features
LEFT JOIN feature_text AS text_en
ON text_en.year = product_features.year
AND text_en.feature = product_features.feature
AND (substr(product_features.product, 1, 2) = text_en.product_group OR text_en.product_group IS NULL OR TRIM(text_en.product_group) = '')
AND text_en.language = 1
LEFT JOIN feature_text AS text_de
ON text_de.year = product_features.year
AND text_de.feature = product_features.feature
AND (substr(product_features.product, 1, 2) = text_de.product_group OR text_de.product_group IS NULL OR TRIM(text_de.product_group) = '')
AND text_de.language = 2

Обратите внимание, что вы все равно получите дубликаты, если будут предоставлены разные описания как для отдельного товара, так и для группы товаров. Если вам нужно разрешить такие дубликаты, то каждое LEFT JOIN необходимо выполнить 2 раза (для продукта и для группы), а затем соответствующее описание должно быть получено с помощью COALESCE.

Результат

Для обновленных записей ваш запрос возвращает 17 строк вместо 11. Все строки с DSP включены несколько раз. См. результат этого запроса

surfmuggle 27.08.2024 13:44

Другие вопросы по теме

Можно ли создавать расширения sqlite на Apple Silicon с помощью clang?
Заполнение нулевых значений в sqlite3 значением той же строки, но следующего столбца в Python
Как увеличить поле, если запись уже существует?
Всем здравствуйте. У меня вопрос относительно php и SQLite. Я пытаюсь сохранить значения форм регистра в базе данных SQLite, и это не работает?
Дамп Python + Sqlite3, исходный код для MariaDB: неизвестное сопоставление: 'utf8mb4_0900_ai_ci' (db noob)
Исправить ограничение внешнего ключа в этой базе данных SQLite3?
Как гарантировать, что вставки принимают только значения, определенные в Enum?
Почему SQLite не может одновременно выполнить предложения WHERE и ORDER BY в представлении?
Чтение/запись фрейма данных Polars со столбцом списка из/в базу данных
Имеет ли значение порядок составного индекса базы данных, если первый столбец запрашивается только с помощью операции равенства?