web-dev-qa-db-ja.com

1つの行に基づいて行のバッチを返します

SQL Server2008に次のようなテーブルがあるとします。

id  |  name  |  qty
-------------------
1   |  john  |    1
2   |  bill  |    3
3   |  mary  |    2
4   |  jill  |    5

このテーブルをクエリして、最大で2の数量のバッチごとに1行を返したいので、クエリの結果は次のようになります。

id  |  name  |  qty
-------------------
1   |  john  |    1
2   |  bill  |    2
2   |  bill  |    1
3   |  mary  |    2
4   |  jill  |    2
4   |  jill  |    2
4   |  jill  |    1

これはカーソルを使わずにきちんと行うことができますか? unpivotを使用してこれは可能ですか?

ちなみに、qty列の最大値は10です。

3
pvdjay

数字の表があれば簡単です。また、qty10を超えることはできないため、必要なのは非常に小さい数値のテーブルだけです。

CREATE TABLE numbers
  ( i int NOT NULL PRIMARY KEY ) ;

INSERT INTO numbers (i)
VALUES (1), (3), (5), (7), (9) ;

結果に必要な行数はqtyの半分(または約半分)であるため、奇数のみが必要です。

クエリ:

SELECT
    t.id, t.name,
    qty = CASE WHEN n.i = 1 AND t.qty % 2 > 0 THEN t.qty % 2 ELSE 2 END
FROM
    tableX AS t
  JOIN
    numbers AS n
  ON
    n.i <= t.qty ;

もちろん、補助テーブルなしでそれを行うこともできます。

SELECT
    t.id, t.name,
    qty = CASE WHEN n.i = 1 AND t.qty % 2 > 0 THEN t.qty % 2 ELSE 2 END
FROM
    tableX AS t
  JOIN
    (VALUES (1), (3), (5), (7), (9)) AS n (i)
  ON
    n.i <= t.qty ;

dbfiddle.uk でテスト済み。

8
ypercubeᵀᴹ

関数を使用して、次のような元のテーブルに適用することもできます。

CREATE FUNCTION NumbersTable (
  @fromNumber int,
  @toNumber int,
  @originalQty int
) RETURNS TABLE
RETURN (

WITH CTE_NumbersTable AS (
  SELECT case when @originalQty = @fromNumber then 1 else 2 end AS i,case when @originalQty = @fromNumber then 1 else 2 end AS i2
  UNION ALL
  SELECT case when i2 +2 < @toNumber then 2 else 1 end as i , i2 = i2 + case when i2 +2 <= @toNumber then 2 else 1 end 
  FROM CTE_NumbersTable
  WHERE
  i2 < @toNumber
)
SELECT i FROM CTE_NumbersTable
)

そしてそれをテーブルで使用します:

select id,name,Quantity2.i as qty from TestNames
cross apply dbo.NumbersTable(1,qty,qty) as Quantity2 
1
S4V1N