2

AccountSegment を行として、2 週間の日付範囲を列ヘッダーとして持つレポートを作成する必要があります。列の値は、関連するセグメント/日付範囲を持つテーブル内のレコード数のカウントになります。

したがって、目的の出力は次のようになります。

AcctSeg  4/9/12-4/20/12   4/23/12-5/4/12   5/7/12-5/18/12
Segment1       100             200              300
Segment2       110             220              330
Segment3       120             230              340

次のクエリは私が望むことを行いますが、非常に非効率的で見苦しく見えます。同じことを達成するためのより良い方法があるかどうか疑問に思っていました:

SELECT
    AccountSegment = S.Segment_Name,
    '4/9/2012 - 4/20/2012' = SUM(CASE WHEN date_start BETWEEN '2012-04-09' AND '2012-04-20' THEN 1 END),
    '4/23/2012 - 5/4/2012' = SUM(CASE WHEN date_start BETWEEN '2012-04-23' AND '2012-05-04' THEN 1 END),
    '5/7/2012 - 5/18/2012' = SUM(CASE WHEN date_start BETWEEN '2012-05-07' AND '2012-05-18' THEN 1 END),
    '5/21/2012 - 6/1/2012' = SUM(CASE WHEN date_start BETWEEN '2012-05-21' AND '2012-06-01' THEN 1 END),
    '6/4/2012 - 6/15/2012' = SUM(CASE WHEN date_start BETWEEN '2012-06-04' AND '2012-06-15' THEN 1 END),
    '6/18/2012 - 6/29/2012' = SUM(CASE WHEN date_start BETWEEN '2012-06-18' AND '2012-06-29' THEN 1 END),
    '7/2/2012 - 7/13/2012' = SUM(CASE WHEN date_start BETWEEN '2012-07-02' AND '2012-07-13' THEN 1 END),
    '7/16/2012 - 7/27/2012' = SUM(CASE WHEN date_start BETWEEN '2012-07-16' AND '2012-07-27' THEN 1 END),
    '7/30/2012 - 8/10/2012' = SUM(CASE WHEN date_start BETWEEN '2012-07-30' AND '2012-08-10' THEN 1 END)
FROM
    dbo.calls C
    JOIN dbo.accounts a ON C.parent_id = a.id
    JOIN dbo.accounts_cstm a2 ON a2.id_c = A.id
    JOIN dbo.Segmentation S ON a2.[2012_segmentation_c] = S.Segment_Num
WHERE
    c.deleted = 0 
GROUP BY
    S.Segment_Name
ORDER BY
    MIN(S.Sort_Order)
4

2 に答える 2

2

問題ないように見えますが、パフォーマンスの改善を 1 つ提案します。

where c.deleted = 0 and
      date_start between  '2012-04-09' AND '2012-08-10'

これにより、集計が必要な行のみに制限されます。. . すべてを空のデータでリストしたい場合を除きます。

ステートメントに追加else 0したいcaseので、NULL の代わりに 0 を表示します。

于 2012-12-12T21:34:55.063 に答える
1

@PaulStock、喜んでそうします。

この手法は、データの取得とセットの操作であるRDMSの長所を生かします。つまり、C#など、より最適化された他のプログラミング言語にイタリングを任せます。

まず最初にIndexTable、私はマスターデータベースに私のものを保持する必要がありますが、これへの書き込みアクセス権がない場合は、必ずデータベースに保持してください。

次のようになります。

Id
 0
 1
 2
...
n

すべてのシナリオに対して十分な数がある場合n、100,000が適切であり、1,000,000がより適切であり、10,000,000がさらに適切です。もちろん、列idにはクラスターインデックスが付けられます。

私はそれをあなたのクエリに直接関連付けるつもりはありません。なぜなら、私はそれを実際には得られず、私はそれを解決するのが面倒だからです。

代わりに、これをと呼ばれるこのテーブルに関連付けますTransactions。ここでは、毎日(または週や月など)に発生したすべてのトランザクションをロールアップします。

Date                       Amount
2012-18-12 04:58:56.453       10
2012-18-12 06:34:21.456      100
etc

次のクエリは、データを日ごとにロールアップします

SELECT  i.Id, SUM(t.Amount) AS DailyTotal
FROM    IndexTable i
        INNER JOIN
        Transactions t ON i.Id=DATEDIFF(DAY, 0, t.Date)
GROUP BY i.Id

このDATEDIFF関数は、2つの日付の間の日付部分の数、この場合は1900-01-01 0:00:00.000(SQLServerではDateTime= 0)とトランザクションの日付(41、261日があった)の間の日数を返します。それ以来-大きなテーブルが必要な理由をご覧ください)

同じ日のすべてのトランザクションは同じ番号になります。週、月、または秒(非常に大きな数)に変更するのは、日付部分を変更するのと同じくらい簡単です。

もちろん、関心のあるデータよりも前である限り、これより後の開始日を入力できますが、パフォーマンスにほとんど違いはありません。

私はINNER JOINここを使用したので、特定の日にトランザクションがない場合、行はありませんが、LEFT JOINこれらの空の日付をNULL合計として指定します(ISNULL0を取得する場合はステートメントを使用します。

正規化されたデータを使用するとPIVOT、必要に応じて、探している出力を取得できます。

于 2012-12-20T04:07:01.627 に答える