Как разработать подзапрос t-sql, чтобы выбрать только одну запись каждый? - PullRequest
1 голос
/ 18 сентября 2011

Я использую SSMS 2008, пытаюсь выбрать только одну строку / клиент. Мне нужно выбрать следующие столбцы: client_name, end_date и program. У некоторых клиентов есть только один клиентский ряд. Но у других есть несколько.

Для клиентов с несколькими строками они обычно имеют разные end_date и program. Например:

CLIENT       PROGRAM        END_DATE
a            b              c
a            d              e
a            f              g
h            d              e
h            f              NULL

Это действительно упрощенная версия фактических данных. Как вы увидите, разные клиенты могут находиться в одной и той же программе («d»). Но один и тот же клиент не может быть в одной программе более одного раза.

Также хитроумно то, что end_date может иметь значение NULL, поэтому, когда я попытался выбрать этих клиентов с> 1 строкой, я добавил оператор HAVING> 1. Но это исключило все мои строки NULL End_date.

Подводя итог, я хочу по одной строке на клиента. Таким образом, эти клиенты имеют всего одну строку + клиенты, перечисленные выше, по следующим критериям:

  • Выберите только строку, в которой End_date является наибольшим или NULL. (В большинстве случаев end_date является нулевым для этих клиентов).

Как я могу достичь этого с как можно меньшим количеством логики?

1 Ответ

4 голосов
/ 18 сентября 2011

В SQL Server 2005 и более поздних версиях вы можете использовать общее табличное выражение (CTE) в сочетании с функциями ROW_NUMBER() и PARTITION BY. Этот CTE «разделит» ваши данные по одному критерию - в вашем случае Client, создав «раздел» для каждого отдельного клиента. ROW_NUMBER() будет затем нумеровать каждый раздел, упорядоченный по другим критериям - здесь я создал DATETIME - и назначать номера от 1 и выше, отдельно для каждого раздела.

Таким образом, в этом случае, упорядочивая по DATETIME DESC, самая новая строка нумеруется как 1 - и это тот факт, который я использую при выборе из CTE. Я использовал здесь функцию ISNULL(), чтобы присвоить тем строкам, которые имеют NULL end_date какое-то произвольное значение, чтобы "привести их в порядок". Я не совсем уверен, правильно ли я понял ваш вопрос: вы хотите выбрать строки NULL вместо строк с данным end_Date или вы хотите отдать приоритет существующему значению end_Date над NULL?

Это выберет самую последнюю строку для каждого клиента (для каждого «раздела» ваших данных):

DECLARE @clients TABLE (Client CHAR(1), Program CHAR(1), END_DATE DATETIME)

INSERT INTO @clients 
VALUES('a', 'b', '20090505'),
('a', 'd', '20100808'),
('a', 'f', '20110303'),
('h', 'd', '20090909'),
('h', 'f', NULL)

;WITH LatestData AS
(
   SELECT Client, Program, End_Date,
       ROW_NUMBER() OVER(PARTITION BY CLient ORDER BY ISNULL(End_Date, '99991231') DESC) AS 'RowNum'
    FROM @clients
)
SELECT Client, Program, End_Date
FROM LatestData 
WHERE RowNum = 1

Результат:

Client  Program  End_Date
   a       f     2011-03-03
   h       f     (NULL)
...