6

我有一组具有“存储”列的记录。我需要基本上将结果集分成 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小提琴

4

5 回答 5

11

试试这个——

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')

查询:

开发艺术#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

开发艺术#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

亚历山大·费多连科:

;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

埃里克:

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

阿米特辛格:

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

安德烈 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

安德烈 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

查询费用:

查询费用

扩展统计:

ExStat

执行统计:

执行统计

查询计划(来自 dbForge Studio for MS SQL):

查询计划

于 2013-06-14T05:28:13.453 回答
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

我的查询不保留每个商店名称的“原始”顺序,但这不是缺陷,因为在关系数据库表中没有排序的概念。如果要保留特定序列,则必须提供要排序的列。

于 2013-06-14T07:31:55.583 回答
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

SQL 小提琴演示

于 2013-06-14T05:37:26.230 回答
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上的演示

于 2013-06-14T06:51:52.927 回答
2

还有另一种选择,这次使用 PIVOT 和 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 Fiddle 演示:http ://sqlfiddle.com/#!3/354df/39 。

请注意,上述查询中的 UNPIVOT 步骤必须在与 PIVOT 操作分开的 SELECT 中完成。这是因为 UNPIVOT 不会生成 IN 列列表中列出的列包含 NULL 的行。但是,您可以将 UNPIVOT 替换为等效技术(如 CROSS APPLY),从而将反透视移动到执行透视的同一子查询:

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 Fiddle 演示:http ://sqlfiddle.com/#!3/354df/40 。

于 2013-06-16T15:47:04.160 回答