SQL Server Получите значения трех лучших записей и отобразите в одной строке на человека - PullRequest
1 голос
/ 30 июня 2011

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

Мои данные выглядят так:

id       co_number  client_no  Client_name  taken_date    taken_value
--------------------------------------------------------------------------
270103   12         1111       John Doe     6/7/11 8:45 AM    108
270100   12         1111       John Doe     5/3/11 10:49 AM   109
270097   12         1111       John Doe     4/4/11 1:58 PM    109
270094   12         1111       John Doe     3/1/11 9:04 AM    106
270091   12         1111       John Doe     2/1/11 8:47 AM    105
270088   12         1111       John Doe     1/4/11 9:10 AM    106
270120   12       2222       Jane Smith    6/7/11 9:06 AM     215
270117   12       2222       Jane Smith    5/3/11 2:01 PM     216
270114   12       2222       Jane Smith   4/4/11 2:08 PM      214
270111   12       2222       Jane Smith    3/1/11 9:27 AM     209
270159   12       3333       John Adams    6/7/11 9:45 AM     205
270156   12       3333       John Adams   5/3/11 2:12 PM      203
270153   12       3333       John Adams    4/4/11 1:42 PM     202
270150   12       3333       John Adams   3/1/11 10:32 AM     198

Я хочу данныедля отображения, как это (Date1 является самым последним, затем Date2, затем Date3):

co#  Name      Date1             Value1 Date2             Value2 Date3             Value3
-------------------------------------------------------------------------------------------
12   John Doe  2011-06-07 08:45  108.0  2011-05-03 10:49  109.0  2011-04-04 13:58  109.0

Вот что у меня есть до сих пор.Это работает, но медленно (для возврата одного co_number требуется 30 секунд), поэтому мне интересно, есть ли лучший, более эффективный способ сделать это.

select 
vmain.co_nmber, vmain.Client_name, vmain.Taken_date, vmain.Taken_value
, (select top 1 Taken_date from vital vdate where vdate.co_nmber=vmain.co_nmber and vdate.Medical_Record_Number=vmain.Medical_Record_Number and vdate.Taken_date < vmain.Taken_date order by vdate.Taken_date desc) as date2
, (select top 1 Taken_value from vital v_value where v_value.co_nmber=vmain.co_nmber and v_value.Medical_Record_Number=vmain.Medical_Record_Number and v_value.Taken_date < vmain.Taken_date order by v_value.Taken_date desc) as value2
, (select top 1 Taken_date from vital vdate where vdate.co_nmber=vmain.co_nmber and vdate.Medical_Record_Number=vmain.Medical_Record_Number and vdate.Taken_date < (select top 1 Taken_date from vital vdate where vdate.co_nmber=vmain.co_nmber and vdate.Medical_Record_Number=vmain.Medical_Record_Number and vdate.Taken_date < vmain.Taken_date order by vdate.Taken_date desc) order by vdate.Taken_date desc) as date3
, (select top 1 Taken_value from vital vvalue where vvalue.co_nmber=vmain.co_nmber and vvalue.Medical_Record_Number=vmain.Medical_Record_Number and vvalue.Taken_date < (select top 1 Taken_date from vital vdate where vdate.co_nmber=vmain.co_nmber and vdate.Medical_Record_Number=vmain.Medical_Record_Number and vdate.Taken_date < vmain.Taken_date  order by vdate.Taken_date desc)  order by vvalue.Taken_date desc) as value3
from vital as vmain 
inner join(
SELECT v.co_nmber, v.Medical_Record_Number, max(v.Taken_date) as Taken_date
FROM Vital v
and v.co_nmber = 12
GROUP BY v.co_nmber, v.Medical_Record_Number 
) as vsub on vsub.co_nmber=vmain.co_nmber and vsub.Medical_Record_Number=vmain.Medical_Record_Number and vsub.Taken_date = vmain.Taken_date
and vmain.co_nmber = 12
order by vmain.co_nmber, vmain.Medical_Record_Number, vmain.Taken_date

Помощь оценена.

Ответы [ 4 ]

1 голос
/ 30 июня 2011

Вы можете нумеровать ваши записи для каждого сотрудника и клиента с помощью row_number.После этого вы можете выбрать первые, а слева присоединиться ко второму и третьему.Должно быть быстрее.

with cVital as (

select  v.co_nmber, v.Medical_Record_Number, v.Client_name,
        v.taken_date, v.taken_value,
        n = row_number() over (partition by v.co_nmber, v.Medical_Record_Number order by v.taken_date desc)
from    Vital v

)
select  [co#]=v1.co_nmber, [Name]=v1.Client_name,
        Date1 = v1.taken_date, Value1 = v1.taken_value,
        Date2 = v3.taken_date, Value2 = v2.taken_value,
        Date3 = v2.taken_date, Value3 = v3.taken_value
from    cVital v1
left join cVital v2
    on  v2.co_nmber = v1.co_nmber
    and v2.Medical_Record_Number = v1.Medical_Record_Number
    and v2.n = 2
left join cVital v3
    on  v3.co_nmber = v1.co_nmber
    and v3.Medical_Record_Number = v1.Medical_Record_Number
    and v3.n = 3
where   v1.n = 1
order by v1.co_nmber, v1.Medical_Record_Number;
1 голос
/ 30 июня 2011

Большой вопрос: «Что заставляет запрос выполняться медленно?»

Правильно ли проиндексирована ваша таблица?

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

0 голосов
/ 30 июня 2011

Медлительность может заключаться в использовании коррелированных подзапросов в операторе Select. Помните, что подзапросы будут выполняться для каждой записи в таблице

Если вы собираетесь получать только фиксированное количество значений, возвращаемых с использованием таблицы Temp (или переменной таблицы) и метода Row_Number () Over, то будет работать:

SELECT ROW_NUMBER() OVER (PARTITION BY TheKey ORDER BY DateValue DESC) RowNum, TheKey, DateValue, OtherValue
INTO tmp
FROM SomeTable
WHERE .....
SELECT x1.TheKey, x1.DateValue date1, x1.OtherValue Value1, x2.DateValue Date2, x2.OtherValue Value2, x3.CreateDate Date3, x3.OtherValue Value3
FROM (SELECT * FROM #tmp WHERE rownum=1) x1
left JOIN (SELECT * FROM #tmp WHERE rownum=2) x2 ON x1.TheKey = x2.TheKey
left JOIN (SELECT * FROM #tmp WHERE rownum=3) x3 ON x1.TheKey = x3.TheKey
0 голосов
/ 30 июня 2011

Хорошо, я обновил свой ответ. Вот решение, которое будет работать на основе запроса, который я сделал ранее:

    DECLARE @Name VARCHAR(100)
    DECLARE @TmpName VARCHAR(100)
    DECLARE @ID INT
    DECLARE @CoNum INT
    DECLARE @Date1 DATETIME
    DECLARE @Date2 DATETIME
    DECLARE @Date3 DATETIME
    DECLARE @Value1 INT
    DECLARE @Value2 INT
    DECLARE @Value3 INT
    DECLARE @IndexValue INT
    DECLARE @SetValue INT
    DECLARE @SetDate DATETIME

    CREATE TABLE #OutputTable (
        co_number INT,
        Client_Name VARCHAR(200),
        Date1 DATETIME,
        Value1 INT,
        Date2 DATETIME,
        Value2 INT,
        Date3 DATETIME,
        Value3 INT
    )

    SELECT DISTINCT Client_name INTO #TempVitalNames FROM vital ORDER BY Client_name

    WHILE (SELECT COUNT(*) FROM #TempVitalNames) > 0
    BEGIN
        SELECT @IndexValue = 0
        SELECT TOP(1) @TmpName = Client_name FROM #TempVitalNames

        SELECT TOP(3) * INTO #TempVital FROM vital WHERE Client_name = @TmpName ORDER BY taken_date DESC

        WHILE (@IndexValue < 3)
        BEGIN       
            SET @Name = (SELECT TOP 1 Client_name FROM #TempVital)
            SET @CoNum = (SELECT TOP 1 co_number FROM #TempVital)
            SELECT TOP 1 @ID = id FROM #TempVital
            SET @SetDate = (SELECT TOP 1 taken_date FROM #TempVital)
            SET @SetValue = (SELECT TOP 1 taken_value FROM #TempVital)
            DELETE FROM #TempVital WHERE id = @ID
            SET @Date1 = CASE WHEN @IndexValue = 0 THEN @SetDate ELSE @Date1 END
            SET @Date2 = CASE WHEN @IndexValue = 1 THEN @SetDate ELSE @Date2 END
            SET @Date3 = CASE WHEN @IndexValue = 2 THEN @SetDate ELSE @Date3 END
            SET @Value1 = CASE WHEN @IndexValue = 0 THEN @SetValue ELSE @Value1 END
            SET @Value2 = CASE WHEN @IndexValue = 1 THEN @SetValue ELSE @Value2 END
            SET @Value3 = CASE WHEN @IndexValue = 2 THEN @SetValue ELSE @Value3 END

            SELECT @IndexValue = @IndexValue + 1                
        END

        INSERT INTO #OutputTable (co_number, Client_Name, Date1, Value1, Date2, Value2, Date3, Value3)
        ( SELECT @CoNum, @Name, @Date1, @Value1, @Date2, @Value2, @Date3, @Value3 )     

        DELETE #TempVitalNames WHERE Client_name = @TmpName
        DROP TABlE #TempVital
    END

    DROP TABLE #TempVitalNames

    SELECT * FROM #OutputTable

    DROP TABLE #OutputTable

Хорошо, Клаудия. Это сначала вытянет все уникальные имена в таблице во временную таблицу. Затем он будет перебирать каждое имя. Внутри итерационного цикла она будет делать то же, что и моя последняя программа, извлекать нужные даты из верхних трех имен и вставлять их в другую временную таблицу. После того как все имена будут обработаны, он выведет содержимое временной таблицы, которая должна быть результатом, который вы ищете. Я запустил этот запрос в моей системе с предоставленными вами данными теста, и он был выполнен менее чем за 1 секунду. Я знаю, что у вас, вероятно, гораздо больше строк, чем мне приходилось работать, но, похоже, это работает довольно хорошо. Если вам нужно что-то еще, дайте мне знать, и я изменю это соответственно Вы можете добавлять любые дополнительные операторы where к операторам SELECT, если это необходимо. Вам также может понадобиться изменить некоторые типы данных. Я не был уверен, было ли ваше take_value целым, десятичным или чем-то еще. Я пошел с INT, но вы можете изменить его в соответствии с вашими потребностями.

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