SQLite ПРИСОЕДИНЯЕТСЯ к нулю, если ID отсутствует в наборе данных

#sql #sqlite

#sql #sqlite

Вопрос:

У меня есть две таблицы, «базовая» и «переопределенная», в базе данных SQLite, которые мне нужно объединить на базе.ИДЕНТИФИКАТОР и переопределение.Базовый идентификатор с минимальным значением (переопределить.Приоритет). Переопределение.BaseID может быть ‘0’, и в этом случае переопределение является «глобальным» и применяется ко всем базовым записям, если не существует переопределяющей записи с базовым идентификатором и имеет равное или меньшее переопределение.Значение приоритета. Также возможно вообще не иметь записи глобального переопределения, что означает, что я хочу вернуть базовую запись только в том случае, если у них есть соответствующее переопределение.Присутствует запись BaseID. Я могу сделать это в памяти или в хранимой процедуре, но есть ли способ сделать все это в одном запросе?

Например, вот некоторые «базовые» данные:

 ID | Name
----------
1  | Test1
2  | Test2
3  | Test3
  

И вот некоторые «переопределенные» данные:

 ID | BaseID | Priority | Authorized
-----------------------------------
1  | 1      | 1        | 0
2  | 3      | 1        | 1
3  | 0      | 2        | 1
  

И вот что я хочу, чтобы запрос возвращал:

 base.ID | base.Name | override.ID | override.Authorized
-------------------------------------------------------
1       | Test1     | 1           | 0
2       | Test2     | 3           | 1
3       | Test3     | 2           | 1
  

Если бы мы удалили переопределение.ID ‘3’ Я бы ожидал следующего:

 base.ID | base.Name | override.ID | override.Authorized
-------------------------------------------------------
1       | Test1     | 1           | 0
3       | Test3     | 2           | 1
  

Комментарии:

1. Покажите некоторые примерные данные и желаемый результат.

2. @CL добавил образец данных!

3. Может ли быть несколько переопределений для одного и того же BaseID ?

4. @CL да, это возможно, поэтому мне нужен тот, у которого наименьшее значение приоритета

5. @CL Я должен также добавить, что если приоритет глобальной строки имеет меньшее значение, чем приоритет неглобальной строки, тогда глобальная строка должна иметь приоритет. Кроме того, возможно иметь несколько глобальных строк с разным приоритетом.

Ответ №1:

Я придумал это:

Сначала найдите глобальное переопределение с наименьшим приоритетом (если оно существует) и присоедините его к базовому набору (что предотвращает возврат любых базовых строк, если нет глобального переопределения), и добавьте ‘.1’ к приоритету:

 SELECT *
FROM base
JOIN (SELECT ID AS OverrideID, BaseID, Authorized, MIN(Priority)   '.1' AS Priority
    FROM override
    WHERE BaseID = 0)
  

Затем найдите неглобальные переопределения с наименьшим приоритетом, сгруппированные по BaseID, внутреннее соединение с базовой таблицей на базе.ИДЕНТИФИКАТОР и добавьте ‘.0’ к приоритету:

 SELECT *
FROM base b
JOIN (SELECT ID AS OverrideID, BaseID, Authorized, MIN(Priority)   '.0' AS Priority
    FROM override
    WHERE BaseID != 0
    GROUP BY BaseID) o ON b.ID = o.BaseID
  

Наконец, ОБЪЕДИНИТЕ два набора данных и выберите значение наименьшего приоритета, сгруппированное по base.ID:

 SELECT *, MIN(Priority)
FROM (SELECT *
    FROM base
    JOIN (SELECT ID AS OverrideID, BaseID, Authorized, MIN(Priority)   '.1' AS Priority
        FROM override
        WHERE BaseID = 0)
    UNION
    SELECT *
    FROM base b
    JOIN (SELECT ID AS OverrideID, BaseID, Authorized, MIN(Priority)   '.0' AS Priority
        FROM override
        WHERE BaseID != 0
        GROUP BY BaseID) o ON b.ID = o.BaseID)
GROUP BY ID;
  

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

Обновить:

Вау. Ну, мне стало немного скучно, и я решил провести рефакторинг. Я смог значительно упростить запрос:

 SELECT *, MIN(override.Priority   CASE WHEN override.BaseID = 0 THEN '.1' ELSE '.0' END)
FROM base
JOIN override ON (override.BaseID = 0 OR override.BaseID = base.ID)
GROUP BY base.ID;
  

Жаль, что мне не нужно было выполнять concat в функции MIN(), и если бы мне не нужно было беспокоиться о тай-брейках, мне бы это не понадобилось, но это кажется отличным решением.

Ответ №2:

Сначала получите строки с наименьшим значением приоритета для каждого базового идентификатора. (Для использования MIN() таким образом требуется SQLite 3.7.11 или более поздней версии.)

 SELECT ID,
       BaseID,
       MIN(Prority) AS Priority,
       Authorized
FROM override;
  

Затем отфильтруйте любую из тех строк, которые являются недопустимыми из-за переопределения gloval:

 ...
WHERE NOT EXISTS (SELECT 1
                  FROM override AS g
                  WHERE g.BaseID   = 0
                    AND g.Priority < override.Priority);
  

Затем это можно просто объединить:

 SELECT base.ID,
       base.Name,
       o.ID,
       o.Authorized
FROM base
JOIN (SELECT ID,
             BaseID,
             MIN(Prority) AS Priority,
             Authorized
      FROM override
      WHERE NOT EXISTS (SELECT 1
                        FROM override AS g
                        WHERE g.BaseID   = 0
                          AND g.Priority < override.Priority)
     ) AS o
     ON base.ID = o.BaseID OR o.BaseID = 0;
  

Комментарии:

1. Хм, не пропустит ли это присоединение базы к переопределениям с нулевыми идентификаторами, поскольку вы присоединяетесь к базе. ID = o.BaseID?