の簡素化(アンチエイリアス)T-SQL場合には記述です。改善可能ですか?

StackOverflow https://stackoverflow.com/questions/951836

  •  11-09-2019
  •  | 
  •  

質問

ご覧の通り、この吸い込みます。他の代替?たってのカラムの別名のグループによる条項が無い.

select count(callid) ,
case
        when callDuration > 0 and callDuration < 30 then 1
        when callDuration >= 30 and callDuration < 60 then 2
        when callDuration >= 60 and callDuration < 120 then 3
        when callDuration >= 120 and callDuration < 180 then 4
        when callDuration >= 180 and callDuration < 240 then 5
        when callDuration >= 240 and callDuration < 300 then 6
        when callDuration >= 300 and callDuration < 360 then 7
        when callDuration >= 360 and callDuration < 420 then 8
        when callDuration >= 420 and callDuration < 480 then 9
        when callDuration >= 480 and callDuration < 540 then 10
        when callDuration >= 540 and callDuration < 600 then 11
        when callDuration >= 600 then 12
end as duration
from callmetatbl
where programid = 1001 and callDuration > 0
group by case
        when callDuration > 0 and callDuration < 30 then 1
        when callDuration >= 30 and callDuration < 60 then 2
        when callDuration >= 60 and callDuration < 120 then 3
        when callDuration >= 120 and callDuration < 180 then 4
        when callDuration >= 180 and callDuration < 240 then 5
        when callDuration >= 240 and callDuration < 300 then 6
        when callDuration >= 300 and callDuration < 360 then 7
        when callDuration >= 360 and callDuration < 420 then 8
        when callDuration >= 420 and callDuration < 480 then 9
        when callDuration >= 480 and callDuration < 540 then 10
        when callDuration >= 540 and callDuration < 600 then 11
        when callDuration >= 600 then 12
end

編集:私たい方にはシングルの場合のソースの場合の修正おねが少ないので、参考になったと考える間隔であろうと変更されるかもしれは自動的にする。

していることができれば一人callDurationであることからfloat一部上場の解決に有効で私用の場合、値の間隔とします。

  • 見のためのパターンの表現の動きをタイムリーかつ可能であればやりがいのある

     case
        when callDuration > 0 AND callDuration < 30 then 1
        when callDuration > 600 then 12
        else floor(callDuration/60) + 2  end
     end as duration
    
  • 使用インラインビューシングルソースの場合

    select count(d.callid), d.duration
    from (   
       select callid
            , case
               when callDuration > 0 AND callDuration < 30 then 1
               when callDuration > 600 then 12
               else floor(callDuration/60) + 2  end
              end as duration
        from callmetatbl
        where programid = 1001
              and callDuration > 0
    ) d
    group by d.duration
    
  • 利用共通のテーブル表現

       with duration_case as (
          select callid ,
          case
            when callDuration > 0 AND callDuration < 30 then 1
            when callDuration > 600 then 12
            else floor(callDuration/60) + 2  end
          end as duration
       from callmetatbl
       where programid = 1001 and callDuration > 0 )
        select count(callid), duration
        from duration_case
        group by duration
    
  • 利用ユーザ定義関数(えい:-))

  • またはルックアップテーブルおよび参加

    DECLARE @t TABLE(durationFrom float, durationTo float, result INT)
    --populate table with values so the query works
    select count(callid) , COALESCE(t.result, 12)
    from callmetatbl JOIN @t AS t ON callDuration >= t.durationFrom 
    AND callDuration < t.durationTo 
    where programid = 1001 and callDuration > 0
    

おかげさまで、私はとても難しい時間を選択受け答えとして多くの対象の異なる部品の問題としたい考えで簡単な質問とを素直に答え:-)、申し訳ありませんの混乱).

役に立ちましたか?

解決

Q: これからのエイリアスでの、GROUP BY節の

その際の一つのアプローチは、インラインビュー。[編集]その答えらRemus Rusanu(+1!) たとえば共通のテーブル表現の達成に同じことをしているのです。[編集]

のインラインビューが簡単な"エイリアス"は、複雑な表現できるその参照によって、GROUP by条項の外側のクエリ:

select count(d.callid)
     , d.duration
  from (select callid
             , case
               when callDuration >= 600 then 12
               when callDuration >= 540 then 11
               when callDuration >= 480 then 10
               when callDuration >= 420 then 9
               when callDuration >= 360 then 8
               when callDuration >= 300 then 7
               when callDuration >= 240 then 6
               when callDuration >= 180 then 5
               when callDuration >= 120 then 4
               when callDuration >=  60 then 3
               when callDuration >=  30 then 2
               when callDuration >    0 then 1
               --else null
               end as duration
             from callmetatbl
            where programid = 1001
              and callDuration > 0
       ) d
group by d.duration

みを開梱します。

  • 内(インデント)クエリーとも呼ばれ、 インラインビュー (まさかの別名 d)
  • 外側のクエリーまで参考のエイリアス duration から d

するのに十分お答えします。今までと同等の交換用表情を一から tekBlues (+1 ! の正解では境界および非整数です。)

の交換用表情からtekBlues(+1!):

select count(d.callid)
     , d.duration
  from (select callid
             , case 
               when callduration >=30 and callduration<600
                    then floor(callduration/60)+2
               when callduration>0 and callduration< 30
                    then 1 
               when callduration>=600
                    then 12
               end as duration
          from callmetatbl
         where programid = 1001
           and callDuration > 0
       ) d
 group by d.duration

(これは十分にお答えします。)


[更新]サンプル ユーザー定義の関数 (交換のためのインラインの場合に表現)

CREATE FUNCTION [dev].[udf_duration](@cd FLOAT)
RETURNS SMALLINT
AS
BEGIN
  DECLARE @bucket SMALLINT
  SET @bucket = 
  CASE
  WHEN @cd >= 600 THEN 12
  WHEN @cd >= 540 THEN 11
  WHEN @cd >= 480 THEN 10
  WHEN @cd >= 420 THEN 9
  WHEN @cd >= 360 THEN 8
  WHEN @cd >= 300 THEN 7
  WHEN @cd >= 240 THEN 6
  WHEN @cd >= 180 THEN 5
  WHEN @cd >= 120 THEN 4
  WHEN @cd >=  60 THEN 3
  WHEN @cd >=  30 THEN 2
  WHEN @cd >    0 THEN 1
  --ELSE NULL
  END
  RETURN @bucket
END

select count(callid)
     , [dev].[udf_duration](callDuration)
  from callmetatbl
 where programid = 1001
   and callDuration > 0
 group by [dev].[udf_duration](callDuration)

注記: こともできますユーザ定義関数の追加オーバーヘッド(コース)追加の依存関係が別のデータベース上のオブジェクトです。

この例では、機能は同等の表現です。OPの場合表現を持たない隙間では参考にそれぞれの"ブレークポイント"など、う検査(けいぶけんさ)のみを下行します。(ケースを返しますが、条件を満足しています。この試験は逆で、独自のレンダリングメカニズムの場合(<=0またはNULL)から抜け落ちなく、 ELSE NULL 必要はなくなり、追加するためにご提供しています。

詳細はこちらから

(必ず確認してください性能とバージョンのオプティマイザのプランは、してください同じではないが大幅に悪化している、オリジナルです。過去においしく述語に押され、インラインビュー、見せていないが問題になるケースがあるようです。

保ビュー

ことに注意 インライン ビューが蓄積することもできるビュー定義のデータベースです。がないこと以外は"非表示"の複雑な表現からです。

簡素化し、複雑な表現

他に複雑な表現が"簡単"ではユーザー定義機能です。ユーザが定義され機能が付属し、独自の設定の問題を含むが劣化します。)

追加データベース"のルックアップテーブル"

その答えをお勧めしを追加する"ルックアップテーブルのデータベースです。いることが必要です。ですがもちろん、意味だしていきたいと考えている異なった値 duration から callDuration, は、フライ、 なし を変更検索すると、 なし ることを実行で異例変更は、ビュー定義は変更するユーザー定義機能)。

と共にルックアップテーブルで、利用するクエリを異なる結果セットだけを行うDMLの"ルックアップ"。

でもこの利点が実際に欠点です。

を十分考慮した場合に実際に比して著しくは下記です。-に与える影響を考慮した新しいテーブルにおいてユニット検査方法を確認する内容のルックアップテーブルを有効とな変更(いず重複?みず?), 影響の継続的なメンテナンスのコードにより追加の複雑性).

一部の大前提

多くの答えがここに掲載されていることによる callDuration 整数であるdatatype.どうやって見るのではない整数、からだったので買ってきたことを含有の問題をもたらしていました。

かなり簡単なテストケースと:

callDuration BETWEEN 0 AND 30

ない 相当

callDuration > 0 AND callDuration < 30

他のヒント

あなたはbetweenを使用していない何らかの理由はありますか? case文自体はそれほど悪くは見えません。あなたが本当にそれを嫌う場合は、テーブルにこのすべてを投げ、それをマップすることができます。

Durations
------------------
low   high   value
0     30     1
31    60     2

など...

(SELECT value FROM Durations WHERE callDuration BETWEEN low AND high) as Duration

編集:または、フロートが使用されているとbetweenが煩雑になった場合に

(SELECT value FROM Durations WHERE callDuration >= low AND callDuration <= high) as Duration

の場合は、次のように書くことができます:

case 
when callduration >=30 and callduration<600 then floor(callduration/60)+2
when callduration>0 and callduration< 30 then 1 
when callduration>=600 then 12
end

、必要に応じてそれを交換しないでされた "どこcallduration> 0"

私は前に与えられた変換テーブルの答えが好き!それが最善の解決策だ。

必要なものを押し込む場合はさらに下のクエリのツリーその投影が見える、グループです。このときの達成には二つの方法:

  1. 利用派生テーブル(スペンサーは、アダムとジェレミー-ラウ)
  2. 共用ステーブル式

    with duration_case as (
    select callid ,
    case
        when callDuration > 0 and callDuration < 30 then 1
        when callDuration >= 30 and callDuration < 60 then 2
        when callDuration >= 60 and callDuration < 120 then 3
        when callDuration >= 120 and callDuration < 180 then 4
        when callDuration >= 180 and callDuration < 240 then 5
        when callDuration >= 240 and callDuration < 300 then 6
        when callDuration >= 300 and callDuration < 360 then 7
        when callDuration >= 360 and callDuration < 420 then 8
        when callDuration >= 420 and callDuration < 480 then 9
        when callDuration >= 480 and callDuration < 540 then 10
        when callDuration >= 540 and callDuration < 600 then 11
        when callDuration >= 600 then 12
    end as duration
    from callmetatbl
    where programid = 1001 and callDuration > 0 )
       select count(callid), duration
       from duration_case
       group by duration
    

両方の解に相当。見CTEsは、読みやすくするためにも好由来表しています。

分割 callDuration 60:

case
        when callDuration between 1 AND 29 then 1
        when callDuration > 600 then 12
        else (callDuration /60) + 2  end
end as duration

ご注意 between ここでは境界がないと思うのを想定しcallDurationとすることとなる整数です。


更新:
せることその他の回答するまで、全体のクエリをこの:

select count(d.callid), d.duration
from (   
       select callid
            , case
                when callDuration between 1 AND 29 then 1
                when callDuration > 600 then 12
                else (callDuration /60) + 2  end
              end as duration
        from callmetatbl
        where programid = 1001
              and callDuration > 0
    ) d
group by d.duration
select count(callid), duration from
(
    select callid ,
    case
            when callDuration > 0 and callDuration < 30 then 1
            when callDuration >= 30 and callDuration < 60 then 2
            when callDuration >= 60 and callDuration < 120 then 3
            when callDuration >= 120 and callDuration < 180 then 4
            when callDuration >= 180 and callDuration < 240 then 5
            when callDuration >= 240 and callDuration < 300 then 6
            when callDuration >= 300 and callDuration < 360 then 7
            when callDuration >= 360 and callDuration < 420 then 8
            when callDuration >= 420 and callDuration < 480 then 9
            when callDuration >= 480 and callDuration < 540 then 10
            when callDuration >= 540 and callDuration < 600 then 11
            when callDuration >= 600 then 12
    end as duration
    from callmetatbl
    where programid = 1001 and callDuration > 0
) source
group by duration

未テスト:

select  count(callid) , duracion
from
    (select 
        callid,
        case        
            when callDuration > 0 and callDuration < 30 then 1        
            when callDuration >= 30 and callDuration < 60 then 2        
            when callDuration >= 60 and callDuration < 120 then 3        
            when callDuration >= 120 and callDuration < 180 then 4        
            when callDuration >= 180 and callDuration < 240 then 5        
            when callDuration >= 240 and callDuration < 300 then 6        
            when callDuration >= 300 and callDuration < 360 then 7        
            when callDuration >= 360 and callDuration < 420 then 8        
            when callDuration >= 420 and callDuration < 480 then 9        
            when callDuration >= 480 and callDuration < 540 then 10        
            when callDuration >= 540 and callDuration < 600 then 11        
            when callDuration >= 600 then 12        
            else 0
        end as duracion
    from callmetatbl
    where programid = 1001) GRP
where duracion > 0
group by duracion

テーブル変数にすべてのケースを追加し、外部結合を行う

DECLARE @t TABLE(durationFrom INT, durationTo INT, result INT)
--        when callDuration > 0 and callDuration < 30 then 1
INSERT INTO @t VALUES(1, 30, 1);
--        when callDuration >= 30 and callDuration < 60 then 2
INSERT INTO @t VALUES(30, 60, 2);

select count(callid) , COALESCE(t.result, 12)
from callmetatbl JOIN @t AS t ON callDuration >= t.durationFrom AND callDuration  < t.durationTo 
where programid = 1001 and callDuration > 0

ここでの私のショットです。必要なコンポーネントのすべてがストレートSQLで行うことができます。

select
  count(1) as total
 ,(fixedDuration / divisor) + adder as duration
from
(
    select
      case/*(30s_increments_else_60s)*/when(callDuration<60)then(120)else(60)end as divisor
     ,case/*(increment_by_1_else_2)*/when(callDuration<30)then(1)else(2)end as adder
     ,(/*duration_capped@600*/callDuration+600-ABS(callDuration-600))/2 as fixedDuration
     ,callDuration
    from 
      callmetatbl
    where
      programid = 1001
    and 
      callDuration > 0
) as foo
group by
  (fixedDuration / divisor) + adder

ここで私がテストに使用されるSQLです。 の(私は私自身の個人callmetatblを持っていません。)

select
  count(1) as total
 ,(fixedDuration / divisor) + adder as duration
from
(
    select
      case/*(30s_increments_else_60s)*/when(callDuration<60)then(120)else(60)end as divisor
     ,case/*(increment_by_1_else_2)*/when(callDuration<30)then(1)else(2)end as adder
     ,(/*duration_capped@600*/callDuration+600-ABS(callDuration-600))/2 as fixedDuration
     ,callDuration
    from -- callmetatbl -- using test view below
      (  
       select 1001 as programid,   0 as callDuration union
       select 1001 as programid,   1 as callDuration union
       select 1001 as programid,  29 as callDuration union
       select 1001 as programid,  30 as callDuration union
       select 1001 as programid,  59 as callDuration union
       select 1001 as programid,  60 as callDuration union
       select 1001 as programid, 119 as callDuration union
       select 1001 as programid, 120 as callDuration union
       select 1001 as programid, 179 as callDuration union
       select 1001 as programid, 180 as callDuration union
       select 1001 as programid, 239 as callDuration union
       select 1001 as programid, 240 as callDuration union
       select 1001 as programid, 299 as callDuration union
       select 1001 as programid, 300 as callDuration union
       select 1001 as programid, 359 as callDuration union
       select 1001 as programid, 360 as callDuration union
       select 1001 as programid, 419 as callDuration union
       select 1001 as programid, 420 as callDuration union
       select 1001 as programid, 479 as callDuration union
       select 1001 as programid, 480 as callDuration union
       select 1001 as programid, 539 as callDuration union
       select 1001 as programid, 540 as callDuration union
       select 1001 as programid, 599 as callDuration union
       select 1001 as programid, 600 as callDuration union
       select 1001 as programid,1000 as callDuration
      ) as callmetatbl
    where
      programid = 1001
    and 
      callDuration > 0
) as foo
group by
  (fixedDuration / divisor) + adder
2つのレコード12を介して各期間(バケット)1について数えて

SQL出力は、以下に示されている。

total  duration
2             1
2             2
2             3
2             4
2             5
2             6
2             7
2             8
2             9
2            10
2            11
2            12

ここで「foo」というサブクエリから結果があります

divisor adder   fixedDuration  callDuration
120         1               1             1
120         1              29            29
120         2              30            30
120         2              59            59
60          2              60            60
60          2             119           119
60          2             120           120
60          2             179           179
60          2             180           180
60          2             239           239
60          2             240           240
60          2             299           299
60          2             300           300
60          2             359           359
60          2             360           360
60          2             419           419
60          2             420           420
60          2             479           479
60          2             480           480
60          2             539           539
60          2             540           540
60          2             599           599
60          2             600           600
60          2             600          1000

乾杯ます。

ここでユーザー定義関数を持つので、間違っているのですか?あなたは、両方の視覚的コードをクリーンアップし、そのような機能を一元化できます。性能面では、私はあなたが本当に遅く何かをやっている場合を除きヒットがあまりにも恐ろしいものを見ることができないと述べた内UDFます。

durationのためのルックアップテーブルを作成します。
ルックアップテーブルを使用することで、同様SELECT文をスピードアップします。

ここでは、ルックアップテーブルでどのように見えるかの最終的な結果がある。

select  count(a.callid), b.ID as duration
from    callmetatbl a
        inner join DurationMap b 
         on a.callDuration >= b.Minimum
        and a.callDuration < IsNUll(b.Maximum, a.CallDuration + 1)
group by  b.ID

ここでルックアップテーブルです。

create table DurationMap (
    ID          int identity(1,1) primary key,
    Minimum     int not null,
    Maximum     int 
)

insert  DurationMap(Minimum, Maximum) select 0,30
insert  DurationMap(Minimum, Maximum) select 30,60
insert  DurationMap(Minimum, Maximum) select 60,120
insert  DurationMap(Minimum, Maximum) select 120,180
insert  DurationMap(Minimum, Maximum) select 180,240
insert  DurationMap(Minimum, Maximum) select 240,300
insert  DurationMap(Minimum, Maximum) select 300,360
insert  DurationMap(Minimum, Maximum) select 360,420
insert  DurationMap(Minimum, Maximum) select 420,480
insert  DurationMap(Minimum, Maximum) select 480,540
insert  DurationMap(Minimum, Maximum) select 540,600
insert  DurationMap(Minimum) select 600
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top