2015-02-24 2 views
2

У меня сложный запрос, который действительно над моей головой. Я думаю, что RANK of RANK-ing нужен, но должен быть лучший и существующий способ.Комплекс RANK в SQL

Здесь у меня есть SimPE таблица:

Manufacturer DateOF Status    Prefer 
Dell   05-2014 ComputerInstalled 30 
Dell   05-2014 ComputerUninstalled 70 
Dell   05-2014 ComputerUninstalled 70 
Dell   05-2014 ComputerUninstalled 70 
Dell   05-2014 ComputerInstalled 30 
Dell   05-2014 ComputerUninstalled 70 
Dell   05-2014 ComputerNew   26 
Dell   05-2014 ComputerNew   26 
Dell   05-2014 ComputerInstalled 30 
Dell   05-2014 ComputerInstalled 30 

Что мне нужно сделать, это GROUP BY таблицы по ПРОИЗВОДИТЕЛЮ и DATEOF колонн, затем выбрать строки с самым низким ПРЕДПОЧИТАЛИ число (26 в этом случае).

Ее легко с помощью функции RANK:

SELECT sq.* 
FROM 
(
SELECT 
*, 
RANK() OVER (PARTITION BY Manufacturer,DateOF ORDER BY Prefer) AS RankPrefer 
FROM 
table1 
WHERE 
RankPrefer = 1 
) sq 

Так что я буду иметь результат 2 ряда с Status ComputerNew.

Manufacturer DateOF Status    Prefer 
Dell   05-2014 ComputerNew   26 
Dell   05-2014 ComputerNew   26 

Это легко, а не вопрос.

Вопрос заключается в том:

Я должен реализовать следующее правило:

Если строки с наименьшим Предпочитают значения (например: 26) оказываются иметь ComputerNew значение в их Состояние поле, , тогда я должен добавить больше строк с ComputerInstalled значений.

Результат должен быть таким:

Manufacturer DateOF Status    Prefer 
Dell   05-2014 ComputerInstalled 30 
Dell   05-2014 ComputerInstalled 30 
Dell   05-2014 ComputerNew   26 
Dell   05-2014 ComputerNew   26 
Dell   05-2014 ComputerInstalled 30 
Dell   05-2014 ComputerInstalled 30 

Подобно этому правилу, у меня есть еще одно:

Если строки с наименьшим Предпочитают значения (например: 26) оказываются имеют ComputerOld значение в их Состояние поле, тогда я должен включить больше строк с ComputerUninstalled val ЕЭС.

Я думаю, что RANK of RANKING разрешит это, но теперь я действительно потерян.

Любая помощь приветствуется в этой загадке.

Спасибо


edit1:

решение Гордона почти хорошо, но не идеально.

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

I включают тестовые данные и здесь:

INSERT Table1 VALUES ('HP10011','04/01/2014','ComputerUninstalled',70) 
INSERT Table1 VALUES ('HP10011','04/04/2014','ComputerOld',26) 
INSERT Table1 VALUES ('HP10011','04/04/2014','ComputerOld',26) 
INSERT Table1 VALUES ('HP10011','04/30/2014','ComputerUninstalled',70) 
INSERT Table1 VALUES ('HP10011','05/23/2014','QuickDispose',10) 
INSERT Table1 VALUES ('HP10011','06/03/2014','QuickDispose',10) 
INSERT Table1 VALUES ('HP10077','04/01/2014','ComputerUninstalled',70) 
INSERT Table1 VALUES ('HP1910','04/25/2014','QuickDispose',10) 
INSERT Table1 VALUES ('HP1910','05/01/2014','ComputerInstalled',30) 
INSERT Table1 VALUES ('HP1910','05/01/2014','ComputerInstalled',30) 
INSERT Table1 VALUES ('HP1910','05/01/2014','ComputerInstalled',30) 
INSERT Table1 VALUES ('HP1910','05/01/2014','ComputerInstalled',30) 
INSERT Table1 VALUES ('HP1910','05/01/2014','ComputerUninstalled',70) 
INSERT Table1 VALUES ('HP1910','05/01/2014','ComputerUninstalled',70) 
INSERT Table1 VALUES ('HP1910','05/01/2014','ComputerUninstalled',70) 
INSERT Table1 VALUES ('HP1910','05/01/2014','ComputerUninstalled',70) 
INSERT Table1 VALUES ('HP1910','05/02/2014','ComputerInstalled',30) 
INSERT Table1 VALUES ('HP1910','05/02/2014','ComputerInstalled',30) 
INSERT Table1 VALUES ('HP3720','05/07/2014','ComputerInstalled',30) 
INSERT Table1 VALUES ('HP3720','05/07/2014','ComputerInstalled',30) 
INSERT Table1 VALUES ('HP3720','05/07/2014','ComputerUninstalled',70) 
INSERT Table1 VALUES ('HP3720','05/07/2014','ComputerUninstalled',70) 
INSERT Table1 VALUES ('HP3720','05/07/2014','ComputerUninstalled',70) 
INSERT Table1 VALUES ('HP3720','05/07/2014','ComputerUninstalled',70) 
INSERT Table1 VALUES ('HP3720','05/08/2014','ComputerInstalled',30) 
INSERT Table1 VALUES ('HP3720','05/08/2014','ComputerInstalled',30) 
INSERT Table1 VALUES ('HP3720','05/08/2014','ComputerInstalled',30) 
INSERT Table1 VALUES ('HP3720','05/08/2014','ComputerUninstalled',70) 
INSERT Table1 VALUES ('HP3720','06/06/2014','ComputerUninstalled',70) 
INSERT Table1 VALUES ('HP3720','06/06/2014','ComputerUninstalled',70) 
INSERT Table1 VALUES ('HP3720','06/10/2014','ComputerOld',26) 
INSERT Table1 VALUES ('HP3720','06/10/2014','ComputerUninstalled',70) 
INSERT Table1 VALUES ('HP3720','06/10/2014','ComputerUninstalled',70) 
INSERT Table1 VALUES ('HP3720','06/11/2014','ComputerOld',26) 
INSERT Table1 VALUES ('HP3720','06/11/2014','ComputerUninstalled',70) 
INSERT Table1 VALUES ('HP3720','06/11/2014','ComputerUninstalled',70) 

Запрос возвращает обе строки ComputerInstalled и ComputerUninstalled для следующих данных:

'HP1910','05/01/2014','ComputerInstalled',30 
'HP1910','05/01/2014','ComputerUninstalled',70 

Он должен выбрать ComputerInstalled только потому, что Производитель, в том же месяце, он должен выбрать самый низкий вариант (30).

Результат для этого набора данных должен быть таким:

Manufacturer DateOF Status   Prefer 
HP10011 2014-04-01 ComputerUninstalled 70 
HP10011 2014-04-04 ComputerOld   26 
HP10011 2014-04-04 ComputerOld   26 
HP10011 2014-04-30 ComputerUninstalled 70 
HP10011 2014-05-23 QuickDispose  10 
HP10011 2014-06-03 QuickDispose  10 
HP10077 2014-04-01 ComputerUninstalled 70 
HP1910 2014-04-25 QuickDispose  10 
HP1910 2014-05-01 ComputerInstalled 30 
HP1910 2014-05-01 ComputerInstalled 30 
HP1910 2014-05-01 ComputerInstalled 30 
HP1910 2014-05-01 ComputerInstalled 30 
HP3720 2014-05-07 ComputerInstalled 30 
HP3720 2014-05-07 ComputerInstalled 30 
HP3720 2014-05-08 ComputerInstalled 30 
HP3720 2014-05-08 ComputerInstalled 30 
HP3720 2014-05-08 ComputerInstalled 30 
HP3720 2014-06-06 ComputerUninstalled 70 
HP3720 2014-06-06 ComputerUninstalled 70 
HP3720 2014-06-10 ComputerOld   26 
HP3720 2014-06-10 ComputerUninstalled 70 
HP3720 2014-06-10 ComputerUninstalled 70 
HP3720 2014-06-11 ComputerOld   26 
HP3720 2014-06-11 ComputerUninstalled 70 
HP3720 2014-06-11 ComputerUninstalled 70 
+0

Вы пишете, вам нужно добавить больше строк. Но на основании какого условия? Нужно ли включать следующие строки, где значение «Предпочтительность» является вторым, большим, чем самое низкое, и «Состояние» = «Установленные компьютером»? – derstauner

+0

Ниже приведены ответы @derstauner. Так что больше строк вставляется только в двух случаях: 1. Когда строки с наименьшим Предположим, что в поле «Статус» есть «ComputerOld» (затем включить строки с «ComputerUninstalled» в поле «Статус»). 2. Такая же логика, но с компьютерами ComputerNew и ComputerInstalled calues. – Avithohol

ответ

3

Вот одна идея. Определите ранжирование предпочтений для строк. Затем определите, соответствуют ли строки с рангом = 1 вашим критериям, используя exists.

Окончательный запрос будет выглядеть следующим образом:

with r as (
     select t.*, 
      rank() over (partition by manufacturer, dateof order by Prefer) as seqnum 
     from table1 t 
    ), 
    r1 as (
     select r.* 
     from r 
     where seqnum = 1 
    ) 
select r.* 
from r 
where r.seqnum = 1 or 
     (exists (select 1 from r1 where status = 'ComputerNew' and r1.dateof = r.dateof) and r.status = 'ComputerInstalled' or 
     exists (select 1 from r1 where status = 'ComputerOld' and r1.dateof = r.dateof) and r.status = 'ComputerUninstalled' 
    ); 
+1

По какой-то причине он не позволит мне сохранять изменения, поэтому я добавлю это в комментарии: это решение должно работать, но я бы рекомендовал окружить каждую из двух последних строк в последнем предложении WHERE круглыми скобками для ясности , а не полагаться на приоритет оператора. Кроме того, вы ссылаетесь на неправильный псевдоним в последнем выборе. Это должно быть 'r. *', А не 't. *'. –

+1

Кроме того, вам нужно только выбрать столбец «Статус» в r1 CTE. В целом, однако, я думаю, что этот ответ - отличный способ решить проблему. –

+0

@GordonLinoff Ваше решение почти хорошо, но я не могу понять, почему в некоторых случаях он терпит неудачу. Я редактировал свой вопрос с помощью раздела Edit1, где я создаю SQL-скрипт и поставляю тестовые данные. – Avithohol

1

Я думаю, что это должно дать вам то, что вы ищете:

WITH sq AS 
(SELECT *, RANK() OVER (PARTITION BY Manufacturer,DateOF ORDER BY Prefer) AS RankPrefer 
    FROM table1 
) 
SELECT * 
FROM sq 
WHERE RankPrefer <= (SELECT TOP 1 RankPrefer FROM sq WHERE Status != 'ComputerNew' ORDER BY RankPrefer) 

Here is the SqlFiddle.

+1

Ваше решение работает с этим точным набором данных, но это просто из-за того, какие значения находятся в столбец «Предпочтение» .Если, например, «ComputerUninstalled» имеет значение «Предпочтение», равное 27, ваше решение вернет все строки «ComputerNew» и «ComputerUninstalled». Кроме того, если «ComputerOld» имеет наименьшее значение «Предпочтение», ваше решение не включает все строки 'ComputerUninstalled'. –

+0

@DeadZone Благодарим вас за запрос, но это действительно так для этого набора данных. – Avithohol

+1

На основе нового редактирования На вопрос, я представил новый ответ. – DeadZone

2

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

;with r as (
     select t.*, 
      CAST(MONTH(dateof) AS VARCHAR(2)) + '-' + CAST(YEAR(dateof) AS VARCHAR(4)) AS EffDate, 
      rank() over (partition by manufacturer, CAST(MONTH(dateof) AS VARCHAR(2)) + '-' + CAST(YEAR(dateof) AS VARCHAR(4)) order by Prefer) as seqnum 
     from Table1 t 
    ), 
    r1 as (
     select r.* 
     from r 
     where seqnum = 1 
    ) 
select r.* 
from r 
where r.seqnum = 1 
or 
(
    r.Status = 'ComputerUninstalled' and 
    exists (Select 1 
      from r1 
      where r1.Manufacturer = r.Manufacturer 
      and r1.EffDate = r.EffDate 
      and r1.Status = 'ComputerOld') 
    and r.seqNum = (Select Min(SeqNum) From r as r2 
         Where r2.Manufacturer = r.Manufacturer 
         And r2.EffDate = r.EffDate 
         And r2.SeqNum > 1)   
) 
or 
(
    r.Status = 'ComputerInstalled' and 
    exists (Select 1 
      from r1 
      where r1.Manufacturer = r.Manufacturer 
      and r1.EffDate = r.EffDate 
      and r1.Status = 'ComputerNew') 
    and r.seqNum = (Select Min(SeqNum) From r as r2 
         Where r2.Manufacturer = r.Manufacturer 
         And r2.EffDate = r.EffDate 
         And r2.SeqNum > 1)   
); 

Примечание: Я получаю еще 2 записи, чем указывает ваш ожидаемый результат. Но из того, что вы описали, я считаю, что вы допустили ошибку в ожидаемых результатах. Есть 6 «ComputerInstalled» для HP1910 в мае 2014 года с преимуществом 30 из них. 4 из них имеют 1 мая в качестве даты, 2 из которых имеют 2 мая. Вы оставили записи 2 мая. Кроме этого, этот результат соответствует вашим ожидаемым результатам и, как мне кажется, должен работать для больших наборов данных.

+0

Непроницаемый, я тоже понял сейчас, и получил тот же вывод. Расширьте предложение WHERE в EXISTS, чтобы сузить и точно совместить строку внутри нашей группы данных, а не внутри всей таблицы. – Avithohol

+0

мое расширение к вашему оригинальному решению было только этим, что также отлично работает: \t \t ... (существует 1 (от 1 из r1, где Производитель = r.Производитель и дата = r.DateOF и Status = 'ComputerNew') и r. status = 'ComputerInstalled') или (существует (выберите 1 из r1, где Производитель = r.Manufacturer и dateof = r.DateOF и status = 'ComputerOld') и r.status = 'ComputerUninstalled') ... – Avithohol

+0

Спасибо @ DeadZone и JonSenchyna и все для этого, я бы обнял вас, ребята, если бы вы были здесь! – Avithohol