Сложный SQL-запрос (по крайней мере, для меня) - PullRequest
3 голосов
/ 02 февраля 2009

Я пытаюсь разработать SQL-запрос, который будет возвращать список серийных номеров. Таблица настроена так, что всякий раз, когда серийный номер достигает шага, вводятся дата и время. Когда он завершает шаг, вводятся другая дата и время. Я хочу разработать запрос, который даст мне список серийных номеров, которые вошли в шаг, но не вышли из него. Они могут вводиться более одного раза, поэтому я ищу только серийные номера, которые не имеют выходов после и введите.

Пример. (Для простоты использования вызывайте таблицу "Table1")

 1. Serial | Step  | Date

 2. 1      | enter | 10/1
 3. 1      | exit  | 10/2
 4. 1      | enter | 10/4
 5. 2      | enter | 10/4
 6. 3      | enter | 10/5
 7. 3      | exit  | 10/6

Для приведенной выше таблицы должны быть получены серийные номера 1 и 2, но не должно быть 3.

Можно ли это сделать в запросе подписи с подзапросами?

Ответы [ 11 ]

4 голосов
/ 02 февраля 2009
select * from Table1 
group by Step 
having count(*) % 2 = 1

это когда не может быть двух «входов», но за каждым вводом следует «выход» (как в приведенном примере)

4 голосов
/ 02 февраля 2009

Лично я думаю, что это лучше всего сделать путем изменения способа хранения данных. Текущий метод не может быть эффективным или действенным. Да, вы можете возиться и найти способ вывести данные. Однако что происходит, когда у вас есть несколько введенных шагов без выхода для одного и того же serialNO? Да, это не должно произойти, но рано или поздно это произойдет, если у вас нет кода, написанного для предотвращения этого (код, который сложно написать). Было бы чище иметь таблицу, в которой хранятся и вход, и выход в одной и той же записи. Тогда это становится тривиальным для запроса (и намного быстрее), чтобы найти те, которые введены, но не вышли.

3 голосов
/ 02 февраля 2009

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

SELECT t1.* 
FROM Table1 t1
LEFT JOIN Table1 t2 ON t2.Serial=t1.Serial 
    AND t2.Step='Exit' AND t2.[Date] >= t1.[Date]
WHERE t1.Step='Enter' AND t2.Serial IS NULL
2 голосов
/ 04 февраля 2009

Я проверял это в MySQL.

SELECT Serial, 
  COUNT(NULLIF(Step,'enter')) AS exits, 
  COUNT(NULLIF(Step,'exit')) AS enters 
FROM Table1
  WHERE Step IN ('enter','exit')
GROUP BY Serial
HAVING enters <> exits

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

1 голос
/ 02 февраля 2009
SELECT * FROM Table1 T1
WHERE NOT EXISTS (
  SELECT * FROM Table1 T2   
  WHERE T2.Serial = T1.Serial 
    AND T2.Step = 'exit'
    AND T2.Date > T1.Date
)
1 голос
/ 02 февраля 2009
SELECT DISTINCT Serial
FROM Table t
WHERE (SELECT COUNT(*) FROM Table t2 WHERE t.Serial = t2.Serial AND Step = 'exit') <
      (SELECT COUNT(*) FROM Table t2 WHERE t.Serial = t2.Serial AND Step = 'enter')
0 голосов
/ 02 февраля 2009

Вот простой запрос, который должен работать с вашим сценарием

SELECT Serial FROM Table1 t1
WHERE Step='enter' 
AND (SELECT Max(Date) FROM Table1 t2 WHERE t2.Serial = t1.Serial) = t1.Date

Я проверил это, и это даст вам строки с серийными номерами 1 и 2

0 голосов
/ 02 февраля 2009

Несколько человек предложили перестроить ваши данные, но я не вижу примеров, поэтому попробую разобраться. Это частично денормализованный вариант той же таблицы, которую вы описали. Он должен хорошо работать с ограниченным числом «шагов» (в этом примере учитываются только «вход» и «выход», но его можно легко развернуть), но его самая большая слабость - добавление дополнительных шагов после заполнения таблицы (скажем, , введите / обработать / выйти) дорого - вам нужно ALTER TABLE, чтобы сделать это.

serial  enter_date  exit_date
------  ----------  ---------
     1        10/1       10/2
     1        10/4       NULL
     2        10/4       NULL
     3        10/5       10/6

Ваш запрос становится довольно простым:

SELECT serial,enter_date FROM table1 WHERE exit_date IS NULL;

serial  enter_date
------  ----------
     1        10/4
     2        10/4
0 голосов
/ 02 февраля 2009

В Oracle:

SELECT *
FROM (
 SELECT serial,
 CASE
   WHEN so < 0 THEN "Stack overflow"
   WHEN depth > 0 THEN "In"
   ELSE "Out"
 END AS stack
 FROM (
  SELECT serial, MIN(SUM(DECODE(step, "enter", 1, "exit", -1) OVER (PARTITION BY serial ORDER BY date)) AS so, SUM(DECODE(step, "enter", 1, "exit", -1)) AS depth
  FROM Table 1
  GROUP BY serial
 )
)
WHERE stack = "Out"

Это выберет то, что вы хотите И отфильтровать exits, что произошло без enters

0 голосов
/ 02 февраля 2009

Если вы используете MS SQL 2005 или 2008, вы можете использовать CTE для получения результатов, которые вы ищете ...

WITH ExitCTE
AS
    (SELECT Serial, StepDate
    FROM    #Table1
    WHERE   Step = 'exit')
SELECT  A.*
FROM    #Table1 A LEFT JOIN ExitCTE B ON A.Serial = B.Serial AND B.StepDate > A.StepDate
WHERE   A.Step = 'enter'
        AND B.Serial IS NULL

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

SELECT  A.*
FROM    #Table1 A LEFT JOIN (SELECT Serial, StepDate
                            FROM    #Table1
                            WHERE   Step = 'exit') B 
            ON A.Serial = B.Serial AND B.StepDate > A.StepDate
WHERE   A.Step = 'enter'
        AND B.Serial IS NULL
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...