在SQL中交替使用不同值的行

时间:2014-05-19 10:38:29

标签: sql sql-server sql-server-2008

下面是一个名为Profile

的表格
AutoId     | GroupId | ProfileId   | ProfileName
-------------------------------------------------   
239        |    54   | abcd        |  name1
251        |    44   | efgh        |  name2 
255        |    54   | ijkl        |  name3
256        |    54   | mnop        |  name4
237        |    44   | qrst        |  name5

以下是名为Group

的表格
GroupId    | IsLive 
--------------------
44         |  1
54         |  0 

我希望在IsLive为1的记录中显示,然后以下记录将在IsLive 1和0的记录之间交替。例如。

AutoId     | GroupId | ProfileId   | ProfileName    
--------------------------------------------------
237        |    44   | qrst        |  name5
251        |    44   | efgh        |  name2 
255        |    54   | ijkl        |  name3
237        |    44   | qrst        |  name5
239        |    54   | abcd        |  name1
251        |    44   | efgh        |  name2 
256        |    54   | mnop        |  name4
237        |    44   | qrst        |  name5

IsLive = 1的记录如果超过IsLive = 0,应该重复。到目前为止,我的查询已经

select AutoId, GroupId, ProfileId, ProfileName
from Profile
where GroupId in (select GroupId from Group where isnull(IsLive,0) = 1)
union all
select AutoId, GroupId, ProfileId, ProfileName
from Profile
where GroupId in (select GroupId from Group where isnull(IsLive,0) <> 1)

上面的查询在顶部给我IsLive = 1,但我无法获得交替的行。任何帮助将不胜感激

2 个答案:

答案 0 :(得分:1)

在问题之后,结果集应该有8行,在这种情况下是这个查询

WITH LiveRows AS (
  SELECT p.AutoID, p.GroupId, p.ProfileID, p.ProfileName
       , ID = Row_Number() OVER (ORDER BY p.AutoId) - 1
       , Rows = COUNT(1) OVER (PARTITION BY NULL)
  FROM   [Profile] p
         INNER JOIN [Group] g ON p.GroupId = g.GroupId
  WHERE  g.IsLive = 1
), DeadRows AS (
  SELECT p.AutoID, p.GroupId, p.ProfileID, p.ProfileName
       , ID = Row_Number() OVER (ORDER BY p.AutoId) - 1
  FROM   [Profile] p
         INNER JOIN [Group] g ON p.GroupId = g.GroupId
  WHERE  g.IsLive = 0
), Ordered As (
SELECT d.AutoID
     , ID = l.Rows + d.ID * 2
FROM   DeadRows d
       INNER JOIN LiveRows l ON l.ID = (d.ID % l.Rows)
UNION ALL
SELECT l.AutoID
     , ID = l.Rows + (d.ID + 1) * 2 - 1
FROM   DeadRows d
       INNER JOIN LiveRows l ON l.ID = (d.ID % l.Rows)
), Total As (
Select p.AutoID, p.GroupId, p.ProfileID, p.ProfileName, p.ID
From   LiveRows p
UNION ALL
Select p.AutoID, p.GroupId, p.ProfileID, p.ProfileName, o.ID
From   Ordered o
       INNER JOIN Profile p ON o.AutoID = p.AutoID
)
Select AutoID, GroupId, ProfileID, ProfileName
FROM   Total
ORDER BY ID

SQLFiddle demo

会得到它。这并不容易,两个CTE LiveRows和DeadRows用于增加可读性。
LiveRows和DeadRows在[Group]的isLive字段上过滤[Profile](对于表名真的是一个糟糕的选择),添加rownumber,并为实时数据添加行数。
行计数在Ordered中使用,连接两个子查询,并创建两行所需行数相同的缺失行。
在Ordered CTE中还计算了将用于结果集的位置ID,因为它将以实时数据开始添加活动行数,公式的另一部分是奇数/偶数对。
总计CTE将具有可用的全局排序字段

我不确定问题的最后两行来自哪里。

答案 1 :(得分:0)

交替值的基本查询是:

with lnl as (
      select AutoId, GroupId, ProfileId, ProfileName,
             row_number() over (partition by islive order by (select NULL)) as seqnum
      from profiles p join
           groups g
           on p.groupid = g.groupid
     )
select AutoId, GroupId, ProfileId, ProfileName
from lnl
order by seqnum, islive;

但是,这不会复制任何值,因此最后一行将是所有相同的类型。

我倾向于减少有效对的行数:

with lnl as (
      select AutoId, GroupId, ProfileId, ProfileName,
             row_number() over (partition by islive order by (select NULL)) as seqnum,
             count(*) over (partition by islive) as cnt
      from profiles p join
           groups g
           on p.groupid = g.groupid
     ),
     cnts as (
      select min(case when islive = 0 then cnt end) as livecnt,
             min(case when islive = 1 then cnt end) as notlivecnt
      from lnl
     )
select AutoId, GroupId, ProfileId, ProfileName
from lnl join
     cnts
     on lnl.seqnum <= livecnt and lnl.seqnum <= notlivecnt
order by seqnum, islive;

复制要困难得多。其中一个问题是其中一个组可能没有任何行,这种可能性使逻辑复杂化。