【问题标题】:SQL limit join/concatenation resultSQL 限制连接/连接结果
【发布时间】:2015-04-20 10:52:29
【问题描述】:

Master

----------------------
| Name      | ID     |
----------------------
| A         | 1      |
| B         | 2      |
| C         | 3      |
----------------------

Detail:

----------------------
| masterID  | det     |
----------------------
| 1         | 21      |
| 1         | 31      |
| 1         | 442     |
| 1         | 76      |
| 2         | 1       |
| 2         | 90      |
| 3         | 48      |
| 3         | 56      |
| 3         | 109     |
----------------------

请求的结果: 进行连接和连接,但将连接值的数量限制为 X(此处为 2),如果更多,则创建一个新行。例如,上面的预期结果将是:

----------------------
| Name      | dets   |
----------------------
| A         | 21, 31 |
| A         | 442, 76|
| B         | 1, 90  |
| C         | 48, 56 |
| C         | 109    |
----------------------

使用下面的代码,我可以得到所有结果的连接,但是我需要关于如何限制连接的记录数量的帮助:

SELECT Master.Name, 
STUFF((
SELECT ','+Detail.det
FROM Detail
WHERE Master.ID = Detail.masterID
FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
,1,2,'')
FROM Master

还为它创建了 SQLFiddle:http://sqlfiddle.com/#!6/a1b69/1/0

【问题讨论】:

  • 哪个 dbms? (这里使用了一些非标准功能......)
  • 这是 SQL Server 2008

标签: sql-server sql-server-2008 for-xml-path


【解决方案1】:

使用row_number() 和整数除法创建一个每隔一行递增的键。在 CTE 中执行此操作,并在主查询和 for xml 查询中使用 CTE。

SQL Fiddle

MS SQL Server 2014 架构设置

create table Master
(
  Name varchar(10),
  ID int
);

create table Detail
(
  masterID int,
  det varchar(10)
);

go

insert into Master values
('A', 1),
('B', 2),
('C', 3);

insert into Detail values
(1, 21 ),
(1, 31 ),
(1, 442),
(1, 76 ),
(2, 1  ),
(2, 90 ),
(3, 48 ),
(3, 56 ),
(3, 109);

查询 1

with C as
(
  select M.Name,
         D.masterID,
         D.det,
         (row_number() over(partition by D.masterID order by D.det) - 1) / 2 as rn
  from dbo.Detail as D
    inner join dbo.Master as M
      on D.masterID = M.ID
)
select C.Name,
       stuff ((select ', ' + D.det
               from C as D
               where C.masterID = D.masterID and
                     C.rn = D.rn
               for xml path(''), type).value('text()[1]', 'varchar(max)'), 1, 2, '')
from C
group by C.masterID,
         C.Name,
         C.rn

Results

| Name |         |
|------|---------|
|    A |  21, 31 |
|    A | 442, 76 |
|    B |   1, 90 |
|    C | 109, 48 |
|    C |      56 |

【讨论】:

    【解决方案2】:

    FOR XML 适用于结果集的结构。我很确定您可以通过在子查询中使用 TOP 2 来做到这一点。

    但是,您可能会发现条件聚合更快:

    select m.name,
           (max(case when seqnum = 1 then cast(id as varchar(255)) else '' end) +
            max(case when seqnum = 2 then ', ' + cast(id as varchar(255)) else '' end) 
           ) as ids
    from master m join
         (select d.*,
                 row_number() over (partition by m.masterId order by (select null)) as seqnum
          from detail d
         ) d
         on m.id = d.masterid
    where seqnum <= 2
    group by name;
    

    或者,因为您似乎并不关心特定的 id,请使用 min 和 max:

    select m.name,
           (case when minid = maxid then cast(minid as varchar(255))
                 else cast(minid as varchar(255)) + ', ' + cast(maxid as varchar(255))
            end) as ids
    from master m join
         (select d.masterid, min(id) as minid, max(id) as maxid
          from detail d
          group by d.masterid
         ) d
         on m.id = d.masterid;
    

    【讨论】:

    • 如果有超过 2 个例如 Name = A 我需要在结果中查看另一条记录。您可以在我的结果表中看到 A 和 C 的情况。 Top 2 将仅返回第一行并忽略其余行。您建议的两个查询也都在做同样的事情
    • @Asha 。 . .啊,我现在明白了。
    猜你喜欢
    • 1970-01-01
    • 2012-08-15
    • 2023-04-02
    • 1970-01-01
    • 2011-01-17
    • 2012-12-13
    • 2013-01-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多