Случайное соединение, когда несколько строк соответствуют критериям соединения - PullRequest
1 голос
/ 28 января 2020

Вот очень упрощенная версия моих данных SQLite:

CREATE Table Questionnaires
(
   questionnaire_id INTEGER NOT NULL,
   seconds INTEGER NOT NULL,
   measure CHAR(4) NOT NULL,
   score INTEGER NOT NULL,
   PRIMARY KEY (questionnaire_id)
)

INSERT INTO Questionnaires (seconds, measure, score) VALUES (5, 'PHQ9', 10), (5, 'GAD7', 8), (20, 'PHQ9', 5)

CREATE Table Events
(
   event_id INTEGER NOT NULL,
   seconds INTEGER NOT NULL,
   PRIMARY KEY (event_id)
)

INSERT INTO Events (seconds) VALUES (5), (5), (10), (15), (20)

Я хотел бы объединить две таблицы на seconds. Моя текущая попытка не дает желаемого результата:

SELECT
    Events.event_id,
    Questionnaires.questionnaire_id,
    Questionnaires.seconds,
    Questionnaires.measure,
    Questionnaires.score
FROM Questionnaires
LEFT OUTER JOIN Events
ON Events.seconds = Questionnaires.seconds

Есть две анкеты на seconds == 5 и два события на seconds == 5, поэтому LEFT OUTER JOIN дает мне четыре результирующих строки, то есть

  1. анкета 1 объединяется с событием 1,
  2. анкета 1 соединяется с событием 2,
  3. анкета 2 соединяется с событием 1, а
  4. анкета 2 присоединяется с событием 2.

Но я хочу либо

  • вопросник 1, объединенный с событием 1, и вопросник 2, соединенный с событием 2, либо
  • вопросник 1 присоединился к событию 2, а вопросник 2 - к событию 1.

Я не против, какое из них я получу.

Я вижу, как это сделать на процедурном языке, но Я не могу понять, как это сделать в SQL теорией множеств c.

Любые идеи?

(NB В моем реальном наборе данных повторяющиеся события совпадения редки, вот почему я не заметил мою глупую ошибку.)

Ответы [ 2 ]

2 голосов
/ 28 января 2020

Вы можете использовать оконную функцию row_number() в обеих таблицах перед объединением:

select
    e.event_id,
    q.questionnaire_id,
    q.seconds,
    q.measure,
    q.score
from (
  select *, row_number() over (partition by seconds order by questionnaire_id) rn
  from Questionnaires
) q left join (
  select *, row_number() over (partition by seconds order by event_id) rn
  from Events
) e on e.seconds = q.seconds and e.rn = q.rn

См. Демоверсию . Или без оконных функций:

SELECT
    e.event_id,
    q.questionnaire_id,
    q.seconds,
    q.measure,
    q.score
FROM Questionnaires q LEFT OUTER JOIN Events e
ON e.seconds = q.seconds
AND (select count(*) from Questionnaires where seconds = q.seconds and questionnaire_id < q.questionnaire_id) =
    (select count(*) from Events where seconds = e.seconds and event_id < e.event_id);

См. demo . Результаты:

| event_id | questionnaire_id | seconds | measure | score |
| -------- | ---------------- | ------- | ------- | ----- |
| 1        | 1                | 5       | PHQ9    | 10    |
| 2        | 2                | 5       | GAD7    | 8     |
| 5        | 3                | 20      | PHQ9    | 5     |
0 голосов
/ 28 января 2020

Вы можете заключить свой выбор в подзапрос и выбрать одну строку, используя ROW_NUMBER():

SELECT eq.event_id, eq.questionnaire_id, eq.seconds,
       eq.measure, eq.score,
FROM (SELECT e.event_id, q.questionnaire_id, q.seconds,
             q.measure, q.score,
             ROW_NUMBER() OVER (PARTITION BY e.event_id ORDER BY e.event_id) as seqnum
      FROM Questionnaires q LEFT JOIN
           Events e
           ON e.seconds = q.seconds
     ) eq
WHERE seqnum = 1;

Если имеется много дубликатов, это может не иметь оптимальной производительности. Но для всего лишь нескольких в каждой таблице это должно быть хорошо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...