2011-12-15 25 views
8

背景:私はダミーデータを作成しながら、いくつかのランダムな「進」の値を取得しようと、この建設を思い付いた:奇妙な行動

SELECT TOP 100 
    result = (CASE ABS(Binary_Checksum(NewID())) % 16 
       WHEN -1 THEN 'hello' 
       WHEN 0 THEN '0' 
       WHEN 1 THEN '1' 
       WHEN 2 THEN '2' 
       WHEN 3 THEN '3' 
       WHEN 4 THEN '4' 
       WHEN 5 THEN '5' 
       WHEN 6 THEN '6' 
       WHEN 7 THEN '7' 
       WHEN 8 THEN '8' 
       WHEN 9 THEN '9' 
       WHEN 10 THEN 'a' 
       WHEN 11 THEN 'b' 
       WHEN 12 THEN 'c' 
       WHEN 13 THEN 'd' 
       WHEN 14 THEN 'e' 
       WHEN 15 THEN 'f' 
       ELSE 'huh' END) 
      FROM sys.objects 

私のSQL Server 2008 R2のインスタンスでこれを実行している場合、

result 
------ 
huh 
3 
huh 
huh 
6 
8 
6 

私は本当に理由を理解していません。 何が起こることを期待することは次のとおりです。すべてのレコードNewID()ため

  • が新しいランダム
  • Binary_Checksum()を思い付くは値
  • ABS()が正の値になるとに基づいて、INTを計算
  • % 16は、16で除算される場合、その正の値の残りの部分を返します。0から15までの値になります。
  • th WHEN sが、ELSEが少なくとも

かを必要とするべきではありません0と15の間のすべての値のためにそこにあるので、電子CASE建設は、関連する文字

  • に値を変換し、それは私が起こるべきだと思うだろうかです...しかし、明らかに何かが道に沿ってうまくいかない...

    (一時テーブルを経由して)2段階のアプローチで同じことをやったとき、ハァッのはなくなっている

    ...

    SELECT TOP 100 x = ABS(Binary_Checksum(NewID())) % 16, 
           result = 'hello' 
        INTO #test 
        FROM sys.objects 
    
    UPDATE #test 
        SET result = (CASE x WHEN 0 THEN '0' WHEN 1 THEN '1' WHEN 2 THEN '2' WHEN 3 THEN '3' 
             WHEN 4 THEN '4' WHEN 5 THEN '5' WHEN 6 THEN '6' WHEN 7 THEN '7' 
             WHEN 8 THEN '8' WHEN 9 THEN '9' WHEN 10 THEN 'a' WHEN 11 THEN 'b' 
             WHEN 12 THEN 'c' WHEN 13 THEN 'd' WHEN 14 THEN 'e' WHEN 15 THEN 'f' 
             ELSE 'huh' END) 
    
    SELECT * FROM #test 
    

    これを理解している人は誰ですか?限り、私はそれを直接または一時テーブルを介してそれに関係なく、私はそれが同じ結果(それは実際にコピー貼りです)を与える必要があると言うことができます...しかし、明らかに何かが間違って行く私は1つのステートメントでそれを行う。

    PS:これには「修正」は必要ありません。私はすでに回避策を講じています(下記参照)。

    回避策:

    SELECT TOP 100 result = SubString('abcdef', 1 + (ABS(Binary_Checksum(NewID())) % 16), 1) 
        FROM sys.objects 
    
  • +0

    PS:面白い可能性のあるSQL Serverの異なるバージョンで誰かがこれをテストできたら... – deroby

    +0

    あなたの回避策はオリジナルよりも簡潔に見えます... – Russell

    +0

    この男を説明するThx ... Damiensの回答これにつまずく可能性のある人にとってはもう少し冗長であるからです。 (そして、彼は一番上に1分速かった= P) – deroby

    答えて

    3

    私はそれ各input_expression = when_expression比較のために、実際に再評価しinput_expression(これは、通常、この場合のように、しない限り、安全であろうと、simple CASE expressionの説明に反し、と信じて、 input_expressionには非決定論的な関数があります)

    したがって、比較のたびに0から15の間で異なる乱数が生成され続け、16個の評価/比較の後にはhuhが出力されます一致する番号を編集しました。

    SELECT TOP 100 
        result = (CASE ABS(Binary_Checksum(Value)) % 16 
          WHEN -1 THEN 'hello' 
          WHEN 0 THEN '0' 
          WHEN 1 THEN '1' 
          WHEN 2 THEN '2' 
          WHEN 3 THEN '3' 
          WHEN 4 THEN '4' 
          WHEN 5 THEN '5' 
          WHEN 6 THEN '6' 
          WHEN 7 THEN '7' 
          WHEN 8 THEN '8' 
          WHEN 9 THEN '9' 
          WHEN 10 THEN 'a' 
          WHEN 11 THEN 'b' 
          WHEN 12 THEN 'c' 
          WHEN 13 THEN 'd' 
          WHEN 14 THEN 'e' 
          WHEN 15 THEN 'f' 
          ELSE 'huh' END) 
          FROM (select NewID() as Value,* from sys.objects) so 
    
    +0

    そうだね。一度あなたがそれを説明すると意味がありますが、私はそれを自分で考えなかったでしょう。どうも。 – deroby

    +1

    あなたのテストでは "これはhuhsを生成しません"が、SQL Serverが一度だけ評価することが保証されていると言うドキュメントを指すことができますか? –

    +2

    これは次のようにする必要があります。 'result =(CASE ABS(Binary_Checksum(Value))%16' – Johan

    8

    計画の計算スカラー

    [Expr1038] =スカラー演算子(CASE WHEN ABS(下記式を有する:


    これはhuh Sを生成しません(16)=(0)THEN '0' ELSE CASE WHEN abs(binary_checksum(newid())) (binary_checksum(new (16)=(2)THEN '2' ELSE CENUE abs(binary_checksum(id)()))%(16)=(1) 0(16)=(4)THEN '4' ELSE CENUE abs(binary_checksum(newid())) 0(16)=(6)THEN '6' ELSE CENUE abs(binary_checksum(newid())) 0(16)=(8)THEN '8' ELSE CENUE abs(binary_checksum(newid())) (16)=(9)THEN '9' ELSEの場合 abs(binary_checksum(newid()))%(16)=(10) newid()))%(16)=(11)THEN 'b' ELSE CASEの場合(16)=(12)THEN 'c' ELSE CASE WHENabs(binary_checksum(newid()))%(16)=(13)abs(binary_checksum(newid()))%(16)=(14)THEN 'f' ELSE 'huh 「ENDのEND ENDのEND ENDエンドのEND ENDエンドのEND ENDエンドのEND ENDエンドのEND END)

    乱数を繰り返し再評価されるのではなく、一度評価されているとCASE文の各支店を通じて一定に保ちます。計画は2つの計算スカラー演算子を持っているのでダミアンの答えで

    (固定)提案された解決策は、私

    SELECT TOP 100 
        result = (CASE ABS(Binary_Checksum(Value)) % 16 
          WHEN -1 THEN 'hello' 
          /*...*/ 
          ELSE 'huh' END) 
          FROM (select NewID() as Value,* from sys.objects) so 
    

    のための作業を行います。定数式Expr1038CASE発現に供給されることを次に定義

    [Expr1038] = Scalar Operator(newid()) 
    

    Plan

    と最初のもの。しかし、この動作が絶対に保証されているかどうかはわかりません。オプティマイザの気まぐれにさらされるかもしれません。

    +0

    LOL、それは確かにそれを説明するでしょう。したがって、内部的にはx 1のときはa 2のときb ...はx = 1のときに変換され、次にx = 2のときは等に変換される。 – deroby

    +0

    @deroby - うん。かなり。これを避けるために100%保証された代替手段があるかどうかは不明です。 –

    +0

    CTE、CROSS APPLYまたはCROSS JOINを使用してこの問題を回避できると思います。誰かがすでに指摘しているように、与えられた回避策は実際にはもっときれいに見えます)= – deroby