Необходимо создать выражение во внешнем объединении, которое возвращает только одну строку.

Я создаю действительно сложный динамический sql, он должен возвращать одну строку для каждого пользователя, но теперь мне нужно объединиться с таблицей «один ко многим». Я делаю внешнее соединение, чтобы убедиться, что я верну хотя бы одну строку (и могу проверить нуль, чтобы увидеть, есть ли данные в этой таблице), но я должен убедиться, что я получаю только одну строку из этой части внешнего соединения, если есть несколько строки в этой второй таблице для этого пользователя. До сих пор я придумал это: (sybase)

SELECT a.user_id
FROM table1 a
    ,table2 b
WHERE a.user_id = b.user_id
AND a.sub_id = (
    SELECT min(c.sub_id)
    FROM table2 c
    WHERE b.sub_id = c.sub_id
    )

Подзапрос находит минимальное значение в таблице «один ко многим» для этого конкретного пользователя.

Это работает, но я опасаюсь неприятностей при выполнении коррелированных подзапросов, когда таблицы 1 и 2 становятся очень большими. Есть ли способ лучше? Я пытаюсь придумать способ заставить соединения делать это, но я этого не вижу. Также слова «где rowcount = 1» или «top 1» мне не помогают, потому что я не пытаюсь исправить вышеуказанный запрос, я ДОБАВЛЯЮ его к уже сложному запросу.


person stu    schedule 27.10.2008    source источник
comment
Этот запрос, как написано, не будет работать. выберите min(c.sub_id) из table2 c, где b.sub_id = c.sub_id всегда будет возвращать b.sub_id, сделайте внешнее предложение where: a.user_id = b.user_id и a.sub_id = b.sub_id   -  person James Curran    schedule 27.10.2008
comment
Это: где a.user_id = b.user_id раньше говорил a.user_id *= b.user_id   -  person stu    schedule 27.10.2008
comment
да, запрос не работает. он всегда будет возвращать b.sub_id. Я думаю, что Джеймс прав, если только вы не пытаетесь сделать что-то другое...   -  person Shawn    schedule 27.10.2008


Ответы (6)


В MySql вы можете убедиться, что любой запрос возвращает не более X строк, используя

select *
from foo
where bar = 1
limit X;

К сожалению, я почти уверен, что это специфичное для MySQL расширение SQL. Однако поиск в Google чего-то вроде «mysql sybase limit» может найти эквивалент для Sybase.

person Dónal    schedule 27.10.2008

Несколько быстрых моментов:

  1. У вас должны быть четкие бизнес-правила. Если запрос возвращает более одной строки, вам нужно подумать о том, почему (помимо просто «это отношение 1:многие — ПОЧЕМУ это отношение 1:много?»). Вы должны придумать бизнес-решение, а не просто использовать « min", потому что это дает вам 1 строку. Бизнес-решение может быть просто "взять первый", и в этом случае min может быть ответом, но вы должны убедиться, что это сознательное решение.
  2. Вы действительно должны попытаться использовать синтаксис ANSI для объединений. Не только потому, что он стандартный, но и потому, что ваш синтаксис на самом деле не делает то, что вы думаете (это не внешнее соединение), и некоторые вещи просто невозможно сделать с вашим синтаксисом.

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

SELECT
     a.user_id, b.*
FROM
     dbo.Table_1 a
LEFT OUTER JOIN dbo.Table_2 b ON b.user_id = a.user_id AND b.sub_id = a.sub_id
LEFT OUTER JOIN dbo.Table_2 c ON c.user_id = a.user_id AND c.sub_id < b.sub_id
WHERE
     c.user_id IS NULL

Вам нужно будет проверить это, чтобы увидеть, действительно ли оно дает то, что вы хотите, и вам, возможно, придется его настроить, но основная идея состоит в том, чтобы использовать второе LEFT OUTER JOIN, чтобы убедиться, что нет строк, которые существуют с более низким sub_id, чем тот, который найден в первом ЛЕВОМ ВНЕШНЕМ СОЕДИНЕНИИ (если он найден). Вы можете настроить критерии во втором LEFT OUTER JOIN в зависимости от окончательных бизнес-правил.

person Tom H    schedule 27.10.2008
comment
Теперь, видите, это то, что я искал. Спасибо. Что касается бизнес-правил, то вот бизнес: базе данных и схеме 8+ лет. Мое новое приложение, которому нужно получить одну строку из таблицы «один ко многим», не будет тем, которое будет изменять схему и все остальные приложения. - person stu; 29.10.2008

Как насчет:

select a.user_id 
from table1 a
where exists (select null from table2 b 
              where a.user_id = b.user_id 
             )
person Tony Andrews    schedule 27.10.2008

Возможно, ваш пример слишком упрощен, но я бы использовал группу:

SELECT
  a.user_id 
FROM 
  table1 a
    LEFT OUTER JOIN table2 b ON (a.user_id = b.user_id)
GROUP BY
  a.user_id

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

Разница между этим запросом и вашим примером заключается в том, что «подтаблица» создается только один раз, однако в вашем примере вы создаете «подтаблицу» для каждой строки в таблице 1 (но это может зависеть от компилятора, поэтому вы можете использовать запрос анализатор для проверки работоспособности).

SELECT
  a.user_id,
  b.sub_id
FROM 
  table1 a
    LEFT OUTER JOIN (
      SELECT
        user_id,
        min(sub_id) as sub_id,
      FROM
        table2
      GROUP BY
        user_id
    ) b ON (a.user_id = b.user_id)

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

Пример временной таблицы:

SELECT
  user_id
INTO
  #table1
FROM 
  table1
WHERE
  .....

SELECT
  a.user_id,
  min(b.sub_id) as sub_id,
INTO
  #table2
FROM
  #table1 a
    INNER JOIN table2 b ON (a.user_id = b.user_id)
GROUP BY
  a.user_id

SELECT
  a.*,
  b.sub_id
from
  #table1 a
    LEFT OUTER JOIN #table2 b ON (a.user_id = b.user_id)
person Ady    schedule 27.10.2008
comment
Я не могу превратить весь запрос в совокупный запрос. - person stu; 27.10.2008
comment
Возможно, вам следует опубликовать свой фактический запрос, чтобы люди могли дать вам полезные ответы. Это был хороший ответ, основанный на том, что вы опубликовали. - person Dave Costa; 27.10.2008

Прежде всего, я считаю, что запрос, который вы пытаетесь написать в качестве примера:

select a.user_id 
from table1 a, table2 b 
where a.user_id = b.user_id 
and b.sub_id = (select min(c.sub_id) 
                from table2 c 
                where b.user_id = c.user_id)

За исключением того, что вам нужно внешнее соединение (которое, я думаю, кто-то отредактировал синтаксис Oracle).

select a.user_id 
from table1 a
left outer join table2 b on a.user_id = b.user_id 
where b.sub_id = (select min(c.sub_id) 
                from table2 c 
                where b.user_id = c.user_id)
person James Curran    schedule 27.10.2008
comment
Да, это отстой, когда кто-то редактирует твой вопрос и меняет смысл. - person stu; 27.10.2008

Ну, у вас уже есть работающий запрос. Если вас беспокоит скорость, вы можете

  • Добавьте поле в таблицу2, которое определяет, какой sub_id является «первым» или

  • Следите за первичным ключом table2 в table1 или в другой таблице

person neonski    schedule 27.10.2008