24

手動で構築したデータ セットで FIRST_VALUE を使用すると、1 つの結果が得られます。左結合の結果であるデータ セットで使用すると、データ セットに含まれているように見えても、別の結果が得られます。まったく同じデータ値。以下の単純なデータセットで問題を再現しました。

私が何かを誤解している場合、誰かが教えてもらえますか?

この SQL は、FIRST_VALUE が NULL で LAST_VALUE が 30 であるという予期された結果を生成します。

SELECT
  agroup,
  aval,
  FIRST_VALUE(aval) OVER (PARTITION BY agroup ORDER BY aval ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) fv,
  LAST_VALUE(aval) OVER (PARTITION BY agroup ORDER BY aval ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) lv
FROM
(
  SELECT 1 agroup, 10 aval
  UNION ALL SELECT 1, NULL
  UNION ALL SELECT 1, 30
) T

この SQL は、上記と同じデータ セットになる LEFT JOIN を使用しますが、FIRST_VALUE は NULL を無視しているように見えます。

SELECT 
  agroup,
  aval,
  FIRST_VALUE(aval) OVER (PARTITION BY agroup ORDER BY aval ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) fv,
  LAST_VALUE(aval) OVER (PARTITION BY agroup ORDER BY aval ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) lv
FROM
(
  SELECT 
    T1.agroup,
    T1.akey,
    T2.aval 
  FROM 
  (
    SELECT 1 agroup, 1 akey
    UNION ALL SELECT 1, 2
    UNION ALL SELECT 1, 3
  ) T1
  LEFT JOIN
  (
    SELECT 1 akey, 10 aval
    UNION ALL SELECT 3,30
  ) T2 ON T1.akey = T2.akey
) T

また、テーブル変数と CTE を使用すると、左結合の動作が異なることも示せます。CTE を使用してデータを生成する場合、FIRST_VALUE は NULL を無視します。まったく同じ SQL を使用しますが、結果をテーブル変数または一時テーブルに入れると、NULL が考慮されます。

CTE を使用すると、SQL Server の結果には FIRST_VALUE の決定に NULL が含まれません。

WITH T AS
(
  SELECT 
    T1.agroup,
    T1.akey,
    T2.aval 
  FROM 
  (
    SELECT 1 agroup, 1 akey
    UNION ALL SELECT 1, 2
    UNION ALL SELECT 1, 3
  ) T1
  LEFT JOIN
  (
    SELECT 1 akey, 10 aval
    UNION ALL SELECT 3,30
  ) T2 ON T1.akey = T2.akey
)

SELECT 
  agroup,
  aval,
  FIRST_VALUE(aval) OVER (PARTITION BY agroup ORDER BY aval ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) fv,
  LAST_VALUE(aval) OVER (PARTITION BY agroup ORDER BY aval ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) lv
FROM
 T

ただし、テーブル変数を使用すると、次のようになります。

DECLARE @T TABLE (agroup INT,akey INT,aval INT)

INSERT INTO
  @T
SELECT 
  T1.agroup,
  T1.akey,
  T2.aval 
FROM 
(
  SELECT 1 agroup, 1 akey
  UNION ALL SELECT 1, 2
  UNION ALL SELECT 1, 3
) T1
LEFT JOIN
(
  SELECT 1 akey, 10 aval
  UNION ALL SELECT 3,30
) T2 ON T1.akey = T2.akey


SELECT 
agroup,
aval,
FIRST_VALUE(aval) OVER (PARTITION BY agroup ORDER BY aval ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) fv,
LAST_VALUE(aval) OVER (PARTITION BY agroup ORDER BY aval ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) lv
FROM
@T
4

2 に答える 2

7

FIRST_VALUE()提供された例は、分析機能の実装に矛盾があることを非常に明確に示しています。

句の基になるテーブルがFROMベース テーブル (または一時テーブル変数、またはオンザフライで作成された派生テーブル) であるかどうかと、オンザフライで作成されたLEFT JOIN2 つのテーブルによって作成された派生テーブル (または cte) に応じて異なります。 2 番目のケースのテーブルでは、結果が異なります。2番目のケースではNULL値が無視されるか、高い値として扱われるようです。

FROMまた、SQL クエリの結果は、句が句に提供するテーブルの値を取得する方法に依存してはならず、句SELECTのドキュメントには値の処理OVER方法が明確に記載されているため、異なるものであってはなりません。NULL

order_by_expression

並べ替える列または式を指定します。order_by_expression は、FROM 句によって使用可能になった列のみを参照できます。列名または別名を表すために整数を指定することはできません。

...

ASC | DESC

指定した列の値を昇順または降順で並べ替えるように指定します。ASC はデフォルトのソート順です。Null 値は可能な限り低い値として扱われます

そのため、SQL-Server のドキュメントによると、正しい結果は NULL 値を無視しないものです。他の結果は発生しないはずであり、実際に発生するため、バグです。

一部のサービス パックまたは更新プログラムで特定および修正されている可能性があるため、(RTM だけでなく) 最新バージョンでテストすることをお勧めします。これを Connect サイトのバグとして送信してください。


アップデート

今後の参考のために、バグは OP によって提出されました。リンクは次のとおりです: Connect アイテムと (私たちの) @Aaron Bertrandは、最新の SQL 2014 ビルドにも表示されるとコメントしています。

于 2013-09-12T19:36:23.013 に答える
-1

この投稿への回答が少し遅れましたが、それでも共有するものです。

order by フラグを使用して、null 値を「下げる」ことができます。

だからあなたの場合...あなたは使うことができます

... FIRST_VALUE(aval) OVER (PARTITION BY agroup ORDER BY (iif(aval is null, 1,0)), aval ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) fv ...

(フィールドを昇順でソートする必要があるため、null 値に値 1 を使用することに注意してください。そのため、null 以外の値が優先されます)

乾杯 - LA.

于 2015-04-10T04:23:44.107 に答える