我有一组记录,其中包含“商店”列。我需要基本上将结果集拆分为13个记录的组,创建空行以填充每个商店以包含13行。 为简单起见,我想说我需要4组记录。
示例,给出下表:
-----------------
Store Name
-----------------
A John
A Bill
B Sam
C James
C Tim
C Chris
D Simon
D Phil
我需要结果如下:
-----------------
Store Name
-----------------
A John
A Bill
A
B Sam
B
B
C James
C Tim
C Chris
D Simon
D Phil
D
纯SQL可以实现这一点吗?每个商店永远不会超过3行。 SQL Fiddle
答案 0 :(得分:9)
试试这个 -
<强> DDL:强>
SET STATISTICS IO ON;
IF OBJECT_ID (N'tempdb.dbo.#temp') IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp
(
Store CHAR(1)
, Name VARCHAR(10)
)
INSERT INTO #temp (Store, Name)
VALUES
('A', 'John'), ('A', 'Bill'),
('B', 'Sam'), ('C', 'James'),
('C', 'Tim'), ('C', 'Chris'),
('D', 'Simon'), ('D', 'Phil')
<强>查询:强>
DevArt#1:
;WITH cte AS
(
SELECT
Store
, Name
, rn = ROW_NUMBER() OVER (PARTITION BY Store ORDER BY (SELECT 1))
FROM #temp
)
SELECT t.Store, Name = ISNULL(t3.Name, '')
FROM (
SELECT DISTINCT Store
FROM cte
) t
CROSS JOIN (SELECT rn = 1 UNION ALL SELECT 2 UNION ALL SELECT 3) t2
LEFT JOIN cte t3 ON t2.rn = t3.rn AND t.Store = t3.Store
DevArt#2:
SELECT t2.Store, Name = ISNULL(t3.Name, '')
FROM (
SELECT *
FROM (
SELECT Store, r = COUNT(1)
FROM #temp
GROUP BY Store
) t
CROSS APPLY (
VALUES (r), (r+1), (r+2)
) t2 (x)
) t2
LEFT JOIN #temp t3 ON t2.Store = t3.Store AND t2.x = t2.r
WHERE t2.x < 4
Alexander Fedorenko:
;WITH cte AS
(
SELECT DISTINCT Store
FROM #temp
)
SELECT o.Store, o.name
FROM cte s
CROSS APPLY (
SELECT TOP 3 x.Store, x.name
FROM (
SELECT s2.Store, s2.name
FROM #temp s2
WHERE s.Store = s2.Store
UNION ALL
SELECT s.Store, ''
UNION ALL
SELECT s.Store, ''
) x
) o
<强> ErikE: 强>
SELECT Store, Name
FROM (
SELECT
x.Store
, x.Name
, s = ROW_NUMBER() OVER (PARTITION BY x.Store ORDER BY x.s)
FROM #temp t
CROSS APPLY (
VALUES
(Store, Name, 0),
(Store, '', 1),
(Store, '', 1)
) x (Store, Name, S)
) z
WHERE s <= 3
ORDER BY Store
<强> AmitSingh: 强>
SELECT t.Store, Name = COALESCE(
(
SELECT name
FROM (
SELECT
row1 = ROW_NUMBER() OVER (PARTITION BY Store ORDER BY Store)
, *
FROM #temp
) c
WHERE t.[row] = c.row1
AND t.Store = c.Store
)
, '')
FROM
(
SELECT
[Row] = ROW_NUMBER() OVER (PARTITION BY a.Store ORDER BY a.Store)
, a.Store
FROM (
SELECT Store
FROM #temp
GROUP BY Store
) a
, (
SELECT TOP 3 Store
FROM #temp
) b
) t
Andriy M#1:
;WITH ranked AS
(
SELECT
Store
, Name
, rnk = ROW_NUMBER() OVER (PARTITION BY Store ORDER BY 1/0)
FROM #temp
)
, pivoted AS
(
SELECT
Store
, [1] = ISNULL([1], '')
, [2] = ISNULL([2], '')
, [3] = ISNULL([3], '')
FROM ranked
PIVOT (
MAX(Name)
FOR rnk IN ([1], [2], [3])
) p
)
, unpivoted AS
(
SELECT
Store
, Name
FROM pivoted
UNPIVOT (
Name FOR rnk IN ([1], [2], [3])
) u
)
SELECT *
FROM unpivoted
Andriy M#2:
;WITH ranked AS
(
SELECT
Store
, Name
, rnk = ROW_NUMBER() OVER (PARTITION BY Store ORDER BY 1/0)
FROM #temp
)
, padded AS
(
SELECT
Store
, Name
FROM ranked
PIVOT (
MAX(Name)
FOR rnk IN ([1], [2], [3])
) p
CROSS APPLY (
VALUES
(ISNULL([1], '')),
(ISNULL([2], '')),
(ISNULL([3], ''))
) x (Name)
)
SELECT *
FROM padded
<强>输出:强>
Store Name
----- ----------
A John
A Bill
A
B Sam
B
B
C James
C Tim
C Chris
D Simon
D Phil
D
<强>统计:强>
Query Presenter Scans Logical Reads
------------------- ----- -------------
DevArt #1 3 41
DevArt #2 2 9
Alexander Fedorenko 4 5
ErikE 1 1
AmitSingh 22 25
Andriy M #1 1 1
Andriy M #2 1 1
查询费用
扩展统计信息:
执行统计信息:
查询计划(来自dbForge Studio for MS SQL):
答案 1 :(得分:4)
Select t.store_id,Coalesce((Select Name from (
Select row_Number() Over(Partition by store_id order by store_id) as row1, * from stores)c
where t.row=c.row1 and t.store_id=c.store_id),'') as cfgg
from
(Select row_Number() Over(Partition by a.store_id order by a.store_id) as row,
a.store_id from
(Select store_id from stores group by store_id) a ,(Select top 3 store_id from stores)b
) t
答案 2 :(得分:4)
APPLY运算符的另一个选项
;WITH cte AS
(
SELECT store_id
FROM stores
GROUP BY store_id
)
SELECT o.store_id, o.name
FROM cte s CROSS APPLY (
SELECT TOP 3 x.store_id, x.name
FROM (
SELECT s2.store_id, s2.name
FROM stores s2
WHERE s.store_id = s2.store_id
UNION ALL
SELECT s.store_id, ''
UNION ALL
SELECT s.store_id, ''
) x
) o
SQLFiddle上的演示
答案 3 :(得分:4)
这是一个有效的查询(SQL Server 2008及更高版本,可以在2005年修复工作):
SELECT Store, Name
FROM (
SELECT
X.Store, X.Name, R = Row_Number() OVER (PARTITION BY X.Store ORDER BY X.S)
FROM
@temp T
CROSS APPLY (VALUES
(Store, Name, 0), (Store, '', 1), (Store, '', 1)
) X (Store, Name, S)
) Z
WHERE R <= 3
ORDER BY Store
;
根据SET STATISTICS IO ON;
,这里有性能统计数据(在这么低的行数下,所有CPU的CPU都可以忽略不计,或许更多行可以帮助确定最佳表现者):
Query Presenter Scans Logical Reads
------------------- ----- -------------
ErikE 1 1
Alexander Fedorenko 4 5
Devart 3 41
AmitSingh 22 25
我的查询不保留每个商店的名称的“原始”排序,但是不是一个缺陷,因为在关系数据库表中没有排序的概念。如果要保留特定序列,必须提供要排序的列。
答案 4 :(得分:2)
又一个选项,这次using PIVOT and UNPIVOT:
WITH ranked AS (
SELECT
store_id,
name,
rnk = ROW_NUMBER() OVER (PARTITION BY store_id ORDER BY 1/0)
FROM stores
),
pivoted AS (
SELECT
store_id,
[1] = ISNULL([1], ''),
[2] = ISNULL([2], ''),
[3] = ISNULL([3], '')
FROM ranked
PIVOT (
MAX(name) FOR rnk IN ([1], [2], [3])
) p
),
unpivoted AS (
SELECT
store_id,
name
FROM pivoted
UNPIVOT (
name FOR rnk IN ([1], [2], [3])
) u
)
SELECT *
FROM unpivoted
;
使用的SQL小提琴演示:http://sqlfiddle.com/#!3/354df/39。
请注意,上述查询中的UNPIVOT步骤必须在与PIVOT操作不同的SELECT中完成。这是因为UNPIVOT不会生成IN列列表中列出的列包含NULL的行。但是,您可以使用等效技术(如CROSS APPLY)替换UNPIVOT,从而将unpivoting移动到进行旋转的同一子查询中:
WITH ranked AS (
SELECT
store_id,
name,
rnk = ROW_NUMBER() OVER (PARTITION BY store_id ORDER BY 1/0)
FROM stores
),
padded AS (
SELECT
store_id,
name
FROM ranked
PIVOT (
MAX(name) FOR rnk IN ([1], [2], [3])
) p
CROSS APPLY (
VALUES
(ISNULL([1], '')),
(ISNULL([2], '')),
(ISNULL([3], ''))
) x (name)
)
SELECT *
FROM padded
;
修改版本的SQL小提琴演示:http://sqlfiddle.com/#!3/354df/40。