4

私はこのテーブルを持っています:

;WITH cte AS (
    SELECT Name='john' , Times=1    
    UNION ALL
    SELECT 'paul' ,2
    UNION ALL
    SELECT 'george' , 3
    UNION ALL
    SELECT 'ringo' , 1
)

ここに画像の説明を入力

各行、Times時間を表示したい:

John 1
Paul 2
Paul 2
george 3
george 3
george 3
ringo 1

だから私は次Cross applyのように書くと知っています:

SELECT *
FROM   cte
       CROSS APPLY(
        SELECT 1 AS ca
        UNION 
        SELECT 2
       ) y

次に、各行が 2 回表示されます。

ここに画像の説明を入力

しかし、私は2回はしたくありません。Times時間が欲しい

質問

それを行うためにクエリを強化するにはどうすればよいですか?

注意:

私の頭に浮かんだ非インテリジェントな解決策は、パラメーターTimesの行を作成するudfを作成することですn-そして、Cross Apply私は単純に次のことを行います: select * from udf_toTable(Times))

4

5 に答える 5

1

クロス適用を使用する必要はありません。むしろ再帰的な CTE を使用します。

;WITH cte AS (
    SELECT Name='john' , Times=1    
    UNION ALL
    SELECT 'paul' , Times=2
    UNION ALL
    SELECT 'george' , Times=3
    UNION ALL
    SELECT 'ringo' , Times=1
)
, res as (
select Name, 1 RowNum
from cte
union all
select cte.Name, res.RowNum+1
from cte
  join res on cte.Name=res.Name
where res.RowNum+1<=cte.Times
)
select res.*, cte.Times
from res
  join cte on cte.Name=res.Name
order by 1, 2

更新 別の動的最大。

;WITH cte AS (
    SELECT Name='john' , Times=1    
    UNION ALL
    SELECT 'paul' , Times=2
    UNION ALL
    SELECT 'george' , Times=3
    UNION ALL
    SELECT 'ringo' , Times=1
), times
AS
(
  select 1 n, MAX(cte.Times) Times
  from cte
  union all
  select t.n+1, t.Times
  from times t
  where t.n+1<=t.Times
)
SELECT 
  c.*
FROM CTE AS c
INNER JOIN times AS t ON c.Times >= t.n
order by 1, 2
于 2015-05-19T11:57:57.573 に答える
0
SELECT A.Name, A.Times
FROM (VALUES
    ('John', 1)
  , ('Paul', 2)
  , ('George', 3)
  , ('Ringo', 1)
) A (Name, Times)
CROSS APPLY(VALUES
    (1), (2), (3)
) B (n)
WHERE A.Times >= B.n
ORDER BY A.Name;

これについて私が考えることができる唯一の欠点は、数値を手動で入力する必要があることですが、これは Numbers Table/TVF で簡単に解決できます。

SELECT A.Name, A.Times
FROM (VALUES
    ('John', 1)
  , ('Paul', 2)
  , ('George', 3)
  , ('Ringo', 1)
) A (Name, Times)
CROSS APPLY dbo.RangeSmallInt(1, A.Times) B;

また、sys.objects の制限に達したこと、そして誰かがシーケンスを生成するための素晴らしいリンクを投稿したことにもコメントで気付きました。私の例で使用されている RangeSmallInt 関数は、その投稿に基づいており、非常にパフォーマンスが高いです。コードは次のとおりです。

-- Generate a range of up to 65,536 contiguous BIGINTS
CREATE FUNCTION dbo.RangeSmallInt (
    @n1 BIGINT = NULL
  , @n2 BIGINT = NULL
)
RETURNS TABLE
AS
RETURN (
    WITH Numbers AS (
        SELECT N FROM(VALUES
            (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 16
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 32
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 48
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 64
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 80
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 96
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 112
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 128
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 144
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 160
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 176
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 192
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 208
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 224
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 240
          , (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1) -- 256
        ) V (N)
    )    
    SELECT TOP (
               CASE
                   WHEN @n1 IS NOT NULL AND @n2 IS NOT NULL THEN ABS(@n2 - @n1) + 1
                   ELSE 0
               END
           )
           N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) - 1 + CASE WHEN @n1 <= @n2 THEN @n1 ELSE @n2 END
    FROM Numbers A, Numbers B
    WHERE ABS(@n2 - @n1) + 1 < 65537
);

サイズ INT をサポートするように拡張する:

-- Generate a range of up to 4,294,967,296 contiguous BIGINTS
CREATE FUNCTION dbo.RangeInt (
    @num1 BIGINT = NULL
  , @num2 BIGINT = NULL
)
RETURNS TABLE
AS
RETURN (
    WITH Numbers(N) AS (
        SELECT N
        FROM dbo.RangeSmallInt(0, 65535)
    )
    SELECT TOP (
               CASE
                   WHEN @num1 IS NOT NULL AND @num2 IS NOT NULL THEN ABS(@num1 - @num2) + 1
                   ELSE 0
               END
           )
           N = ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) + CASE WHEN @num1 <= @num2 THEN @num1 ELSE @num2 END - 1
    FROM Numbers A
       , Numbers B 
    WHERE ABS(@num1 - @num2) + 1 < 4294967297
);
于 2015-05-19T13:54:31.480 に答える
0

私はこれを行うことができました:(sys.objectsのせいでまだ悲しい顔をしています)

;WITH cte AS (
    SELECT Name='john' , Times=1    
    UNION ALL
    SELECT 'paul' ,2
    UNION ALL
    SELECT 'george' , 3
    UNION ALL
    SELECT 'ringo' , 1
)  

SELECT *
FROM   cte
       CROSS APPLY(
                select top (cte.Times) 'bla'=1 from sys.objects
       ) y

update 、答えを見た後:これは CROSS APPLY でそれらを使用するソリューションです:

;WITH cte AS (
    SELECT Name='john' , Times=1    
    UNION ALL
    SELECT 'paul' ,2
    UNION ALL
    SELECT 'george' , 3
    UNION ALL
    SELECT 'ringo' , 1
)   ,  times
AS
(
  select 1 n, MAX(cte.Times) Times
  from cte
  union all
  select t.n+1, t.Times
  from times t
  where t.n+1<=t.Times
)

SELECT *
FROM   cte
       CROSS APPLY(  
                select top (cte.Times) n from times
       ) y
于 2015-05-19T12:15:44.957 に答える