2011-08-25 30 views
5

tôi có một bảng với bố cục sau đây.tính tổng cho các giá trị trong SQL để hiển thị mỗi tháng tên

Email Blast Table 

EmailBlastId | FrequencyId | UserId 
--------------------------------- 
1   | 5   | 1 
2   | 2   | 1 
3   | 4   | 1 


Frequency Table 

Id | Frequency 
------------ 
1 | Daily 
2 | Weekly 
3 | Monthly 
4 | Quarterly 
5 | Bi-weekly 

Tôi cần hiển thị lưới trên trang asp.net của mình như sau.

Email blasts per month. 

UserId | Jan | Feb | Mar | Apr |..... Dec | Cumulative 
----------------------------------------------------- 
1  7  6  6  7   6  #xx 

Cách duy nhất tôi có thể nghĩ là làm như sau, mỗi tháng có tuyên bố trường hợp.

select SUM(
     CASE WHEN FrequencyId = 1 THEN 31 
     WHEN FrequencyId = 2 THEN 4 
     WHEN FrequencyId = 3 THEN 1 
     WHEN FrequencyId = 4 THEN 1 
     WHEN FrequencyId = 5 THEN 2 END) AS Jan, 
     SUM(
     CASE WHEN FrequencyId = 1 THEN 28 (29 - leap year) 
     WHEN FrequencyId = 2 THEN 4 
     WHEN FrequencyId = 3 THEN 1 
     WHEN FrequencyId = 4 THEN 0 
     WHEN FrequencyId = 5 THEN 2 END) AS Feb, etc etc 
FROM EmailBlast 
Group BY UserId 

Bất kỳ cách nào khác tốt hơn để đạt được điều tương tự?

+0

Bạn có chắc chắn hiển thị đúng dữ liệu của mình không? Chỉ vì một cái gì đó được cấu hình để đi ra ngoài một thời gian nhất định một khoảng thời gian không có nghĩa là nó đã làm. Tôi có thể sai về những gì bạn đang làm nhưng tôi nghĩ sẽ tốt hơn nếu đếm số vụ nổ thực tế mỗi tháng ... – NotMe

+0

Trường hợp cuối cùng của bạn không nên là WHEN FrequencyId = 5 THEN 2 –

+0

@chris, Những vụ nổ thực sự có thể lên lịch. Màn hình hiển thị số lần email sẽ được gửi cùng với tần số hiện tại. Tôi tin tưởng, đây là một yêu cầu nên không có điểm cố gắng để xem điều đó đúng hay sai. đã từng trải qua rồi. –

Trả lời

2

Đây có phải là bất kỳ năm nào không? Tôi sẽ giả sử bạn muốn lịch trình cho năm hiện tại. Nếu bạn muốn một năm trong tương lai, bạn luôn có thể thay đổi DECLARE @now để chỉ định bất kỳ ngày nào trong tương lai.

"Một lần trong 2 tuần" (thường được gọi là "hai tuần") không phù hợp với nhóm hàng tháng (ngoại trừ tháng 2 trong năm không phải là năm nhuận). Có nên thay đổi thành "Hai lần một tháng" không?

Ngoài ra, tại sao không lưu hệ số trong bảng Tần suất, thêm cột có tên "PerMonth"? Sau đó, bạn chỉ phải đối phó với các trường hợp hàng ngày và hàng quý (và nó là một sự lựa chọn tùy ý rằng điều này sẽ xảy ra chỉ trong tháng Giêng, tháng tư, và như vậy?).

Giả sử rằng một số trong số này là linh hoạt, đây là những gì tôi sẽ đề nghị, giả sử thay đổi này rất nhỏ cho schema bảng:

USE tempdb; 
GO 

CREATE TABLE dbo.Frequency 
(
    Id INT PRIMARY KEY, 
    Frequency VARCHAR(32), 
    PerMonth TINYINT 
); 

CREATE TABLE dbo.EmailBlast 
(
    Id INT, 
    FrequencyId INT, 
    UserId INT 
); 

Và dữ liệu mẫu này:

INSERT dbo.Frequency(Id, Frequency, PerMonth) 
    SELECT 1, 'Daily', NULL 
    UNION ALL SELECT 2, 'Weekly', 4 
    UNION ALL SELECT 3, 'Monthly', 1 
    UNION ALL SELECT 4, 'Quarterly', NULL 
    UNION ALL SELECT 5, 'Twice a month', 2; 

INSERT dbo.EmailBlast(Id, FrequencyId, UserId) 
    SELECT 1, 5, 1 
    UNION ALL SELECT 2, 2, 1 
    UNION ALL SELECT 3, 4, 1; 

Chúng ta có thể thực hiện điều này bằng cách sử dụng truy vấn rất phức tạp (nhưng chúng tôi không phải mã hóa cứng những tháng đó):

DECLARE @now DATE = CURRENT_TIMESTAMP; 
DECLARE @Jan1 DATE = DATEADD(MONTH, 1-MONTH(@now), DATEADD(DAY, 1-DAY(@now), @now)); 

WITH n(m) AS 
(
    SELECT TOP 12 m = number 
     FROM master.dbo.spt_values 
     WHERE number > 0 GROUP BY number 
), 
months(MNum, MName, StartDate, NumDays) AS 
( SELECT m, mn = CONVERT(CHAR(3), DATENAME(MONTH, DATEADD(MONTH, m-1, @Jan1))), 
     DATEADD(MONTH, m-1, @Jan1), 
     DATEDIFF(DAY, DATEADD(MONTH, m-1, @Jan1), DATEADD(MONTH, m, @Jan1)) 
    FROM n 
), 
grp AS 
(
    SELECT UserId, MName, c = SUM (
     CASE x.Id WHEN 1 THEN NumDays 
      WHEN 4 THEN CASE WHEN MNum % 3 = 1 THEN 1 ELSE 0 END 
      ELSE x.PerMonth END) 
    FROM months CROSS JOIN (SELECT e.UserId, f.* 
     FROM EmailBlast AS e 
     INNER JOIN Frequency AS f 
     ON e.FrequencyId = f.Id) AS x 
    GROUP BY UserId, MName 
), 
cumulative(UserId, total) AS 
(
    SELECT UserId, SUM(c) 
     FROM grp GROUP BY UserID 
), 
pivoted AS 
(
    SELECT * FROM (SELECT UserId, c, MName FROM grp) AS grp 
    PIVOT(MAX(c) FOR MName IN (
     [Jan],[Feb],[Mar],[Apr],[May],[Jun],[Jul],[Aug],[Sep],[Oct],[Nov],[Dec]) 
    ) AS pvt 
) 
SELECT p.*, c.total 
    FROM pivoted AS p 
    LEFT OUTER JOIN cumulative AS c 
    ON p.UserId = c.UserId; 

Kết quả:

UserId Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec total 
1  7 6 6 7 6 6 7 6 6 7 6 6 76 

Dọn dẹp:

DROP TABLE dbo.EmailBlast, dbo.Frequency; 
GO 

Trong thực tế, sự thay đổi schema tôi đề nghị không thực sự mua bạn nhiều, nó chỉ giúp bạn tiết kiệm thêm hai CASE chi nhánh bên trong grp CTE. Đậu phộng, tổng thể.

+0

đầu của tôi đang bùng nổ với tất cả số lượng suy nghĩ tôi cần phải đặt vào điều này :) .. cảm ơn, tôi sẽ nghỉ ngơi và xem xét điều này sau ngày hôm nay. –

+1

Làm tốt lắm Aaron. Bạn đã cho anh ta nhiều thứ hơn để làm việc cùng. –

+0

Tôi sẽ chấp nhận câu trả lời này ngay bây giờ. Nó trông giống như giải pháp gần nhất. Mặc dù tôi sẽ cố gắng và yêu cầu thay đổi thành một con số ước tính, có thể không chính xác 100%. Đẹp sql với hầu hết những điều tôi đã không nhìn thấy. –

3

Tôi nghĩ bạn sẽ kết thúc với một logic phức tạp hơn rất nhiều. Chắc chắn Jan có 31 ngày .. nhưng tháng Hai không ... và tháng Hai thay đổi tùy theo năm. Tiếp theo, các vụ nổ email được gửi ngay cả vào cuối tuần và ngày lễ hoặc là một số ngày bị bỏ qua vì nhiều lý do khác nhau ... Nếu đó là trường hợp thì số ngày làm việc của một tháng cụ thể thay đổi mỗi năm.

Tiếp theo số tuần đầy đủ trong một tháng nhất định cũng thay đổi theo năm. Điều gì xảy ra với những người thêm 4 tuần rưỡi? Họ có đi vào tháng hiện tại hay tháng sau không? Bạn đang sử dụng phương pháp nào để xác định phương pháp đó? Đối với một ví dụ về cách phức tạp này được đọc: http://en.wikipedia.org/wiki/ISO_week_date Cụ thể là phần mà nó nói về tuần đầu tiên, mà thực sự có 9 định nghĩa khác nhau.

Tôi thường không phải là người để nói điều này, nhưng bạn có thể viết tốt hơn bằng mã thông thường thay vì truy vấn sql. Chỉ cần phát hành 'chọn * từ emailblast nơi userid = xxx' và chuyển đổi nó bằng nhiều phương thức mã.

+0

khá trung thực, tôi cũng thua lỗ ở đây. Có lẽ tốt hơn để làm điều đó trong mã, nhưng làm thế nào, nó sẽ không có cùng một vấn đề. Đã không nghĩ rằng nó sẽ phức tạp này khi tôi nhìn nó đầu tiên. –

+0

@ Alex J: Vâng, bạn có cùng một vấn đề. Chỉ cần các công cụ tốt hơn/nhanh hơn để xử lý nó. – NotMe

2

Bạn có thể muốn xem xét thêm bảng thứ 3 có tên là Lịch biểu.

Bạn có thể cấu trúc nó như thế này:

MONTH_NAME 
DAILY_COUNT 
WEEKLY_COUNT 
MONTHLY_COUNT 
QUARTERLY_COUNT 
BIWEEKLY_COUNT 

Kỷ lục JAN sẽ

JAN 
31 
4 
1 
1 
2 

Hoặc bạn có thể cấu trúc nó như thế này:

và có nhiều hồ sơ cho mỗi tháng:

JAN 1 31 
JAN 2 4 
JAN 3 1 
JAN 4 1 
JAN 5 2 

Tôi cho phép bạn tìm hiểu xem logic có lấy được hay không tốt hơn cấu trúc CASE của bạn.

+1

Bạn cần một tài khoản cho mỗi năm để tính toán vào tháng 2 và cũng có tuần nào về mặt kỹ thuật sẽ giảm trong mỗi tháng từ năm này sang năm khác. –

+1

Đúng. Rất nhiều bảo trì với số lượng thay đổi dựa trên lịch, nhưng có lẽ tốt hơn để làm điều đó trong một bảng thay vì mã. –

+0

Vâng, mã luôn có thể cho biết năm đó là năm nào, hoặc bạn có thể cho biết năm nào sẽ sử dụng nếu bạn không muốn số này tính vào tháng 2 của năm hiện tại. –

3

Phụ thuộc vào những gì bạn đang tìm kiếm. Đề xuất 1 sẽ theo dõi vụ nổ email thực tế của bạn (có ngày :-).

Không có ngày thực tế, bất kỳ điều gì bạn đến với một tháng sẽ giống nhau cho mỗi tháng.

Dù sao, nếu bạn định khái quát hóa, thì tôi khuyên bạn nên sử dụng cái gì đó khác với ints - như có thể là phao hoặc số thập phân. Vì đầu ra của bạn dựa trên các bảng được liệt kê trong bài đăng của bạn chỉ có thể gần đúng những gì thực sự xảy ra (ví dụ, tháng 1 thực sự có 4-1/2 tuần, chứ không phải 4), bạn sẽ có một lỗi giới hạn trong bất kỳ phạm vi tháng nào - - trở nên tồi tệ hơn, bạn càng xa ngoại suy. Ví dụ: nếu bạn xuất toàn bộ 12 tháng, ngoại suy của bạn sẽ được ước tính dưới 4 tuần.

Nếu bạn sử dụng phao hoặc số thập phân, thì bạn sẽ có thể đến gần hơn với những gì thực sự xảy ra. Đối với người mới bắt đầu: tìm một đơn vị đo lường chung (tôi khuyên bạn nên sử dụng "ngày") Ví dụ: 1 tháng = 365/12 ngày; 1 quý = 365/4 ngày; 1 2 tuần = 14 ngày; v.v.

Nếu bạn làm điều đó - thì người dùng của bạn có 1 người mỗi quý thực sự có 1 trên 91,25 ngày; 1 mỗi tuần biến thành 1 mỗi 7 ngày; 1 mỗi BiWeek biến thành 1 trong 14 ngày.

**EDIT** -- Incidentally, you could store the per-day value in your reference table, so you didn't have to calculate it each time. For example: 
Frequency Table 

Id | Frequency   | Value 
------------------------------- 
1 | Daily   | 1.0 
2 | Weekly   | .14286 
3 | Monthly   | .03288 
4 | Quarterly  | .01096 
5 | Once in 2 weeks | .07143 

Bây giờ làm toán - (1/91,25 + 1/7 + 1/14) cần một denom chung (như có lẽ 91,25 * 14), vì vậy nó trở thành (14/1277,5 + 182,5/1277,5 + 91,25 /1277.5).

Điều đó bổ sung lên 287.75/1277.5 hoặc .225 email mỗi ngày.

Vì có 365/12 ngày mỗi tháng, nhiều .225 * (365/12) để nhận được 6,85 email mỗi tháng.

đầu ra của bạn sau đó sẽ giống như thế này:

Email blasts per month. 

UserId | Jan | Feb | Mar | Apr |..... Dec | Cumulative 
----------------------------------------------------- 
1  6.85 6.85 6.85 6.85  6.85  #xx 

Toán học có thể có vẻ một chút tẻ nhạt, nhưng một khi bạn bước ra trên mã của bạn, bạn sẽ không bao giờ phải làm điều đó một lần nữa. Kết quả của bạn sẽ chính xác hơn (tôi làm tròn đến 2 chữ số thập phân, nhưng bạn có thể đi xa hơn nếu bạn muốn). Và nếu công ty của bạn đang sử dụng dữ liệu này để xác định ngân sách/thu nhập tiềm năng cho năm sắp tới, điều đó có thể đáng giá. Cũng cần lưu ý là sau khi bạn hoàn thành ngoại suy (và các giới hạn lỗi đòi hỏi), người tiêu dùng của bạn về đầu ra này sẽ làm việc ngoại suy của riêng họ, chứ không phải trên dữ liệu thô, mà là trên đầu ra của bạn. Vì vậy, đó là loại một cú đúp của lỗi giới hạn. Bạn càng chính xác hơn, những con số này càng đáng tin cậy ở mỗi cấp độ tiếp theo.

+0

Tại sao bạn sử dụng 'FLOAT' thay vì' DECIMAL'? Tôi không nghĩ rằng bạn cần mức độ xấp xỉ đó. :-) –

+0

@Aaron - điểm tốt - tôi đoán nếu bạn cần phải chuyển đổi giá trị calc'd trở lại tần số, số thập phân sẽ tốt hơn. Nhưng một trong hai cách ... miễn là nó không phải là một int :-). – Chains

Các vấn đề liên quan