Bagian paling kompleks dari ini adalah hanya membangun kalender dalam format itu. Berputar dan mengelilinginya dengan HTML cukup mudah. Pertama, mari kita mulai dengan ini, tabel karyawan Anda dengan tanggal cuti. leave_type
tampaknya tidak relevan dengan masalah yang dihadapi.
CREATE TABLE dbo.EmpLeave
(
EmployeeID int,
leave_date date,
leave_type_color char(6)
);
INSERT dbo.EmpLeave(EmployeeID,leave_date,leave_type_color)
VALUES(1,'2018-01-02','7777cc'),(1,'2018-04-01','ffffac');
Prosedur yang saya buat tampak seperti ini (dan peringatan: diasumsikan @@DATEFIRST = 7
):
CREATE PROCEDURE dbo.BuildLeaveHTMLTable
@EmployeeID int,
@Year smallint = NULL
AS
BEGIN
SET NOCOUNT ON;
SET @Year = COALESCE(@Year, DATEPART(YEAR, GETDATE()));
DECLARE @FirstDay date = DATEADD(YEAR, @Year-1900, 0);
;WITH Numbers AS ( -- 366 possible days (leap year)
SELECT n = 1 UNION ALL SELECT n + 1 FROM Numbers WHERE n <= 365
),
Calendar AS ( -- a year's worth of dates and dateparts
SELECT [Date] = d,
MonthStart = DATEADD(DAY, 1-DAY(d),d),
Y = CONVERT(smallint, DATEPART(YEAR, d)),
M = CONVERT(tinyint, DATEPART(MONTH, d)),
D = CONVERT(tinyint, DATEPART(DAY, d)),
WY = CONVERT(tinyint, DATEPART(WEEK, d)),
DW = CONVERT(tinyint, DATEPART(WEEKDAY,d))
FROM
(
SELECT d = CONVERT(date,DATEADD(DAY, n-1, @FirstDay)) FROM Numbers
) AS c WHERE YEAR(d) = @Year -- in case it's not a leap year
),
BaseSlots AS ( -- base set of 37 ints
-- month can be spread across 6 weeks, but no more than 2 days in 6th week
SELECT TOP (37) slot = n FROM Numbers ORDER BY n
),
Months AS ( -- base set of 12 ints
SELECT TOP (12) m = slot FROM BaseSlots ORDER BY slot
),
SlotAlignment AS ( -- align days of week to slot numbers
-- this is the most cryptic part of this solution
-- determines which set of 7 slots, and which slot
-- exactly, a given date will appear under
SELECT c.*, slot = DW+(c.WY+1-DATEPART(WEEK,c.MonthStart)-1)*7
FROM Calendar AS c
INNER JOIN Months AS m ON c.M = m.m
),
SlotMatrix AS ( -- extrapolate actual dates to 37 x 12 matrix
SELECT m.m, s.slot, sa.[Date]
FROM BaseSlots AS s
CROSS JOIN Months AS m
LEFT OUTER JOIN SlotAlignment AS sa
ON sa.m = m.m AND sa.slot = s.slot
),
FinalHTML AS ( -- build some HTML!
SELECT m = '<!-- ' + RIGHT('0' + RTRIM(m), 2) + ' -->',
slot, cell = CASE WHEN slot = 1 THEN '<tr><th>'
+ COALESCE(DATENAME(MONTH,DATEADD(MONTH, m-1, 0)),'')
+ '</th>' ELSE '' END + '<td' + COALESCE(' bgcolor=#'
+ RIGHT(CONVERT(varchar(10),CONVERT(varbinary(8), el.leave_type_color),1),6),
CASE WHEN DATEPART(WEEKDAY, [Date]) IN (1,7)
THEN ' bgcolor=#cccccc' ELSE '' END)
+ '>' + COALESCE(RTRIM(DATEPART(DAY,[Date])), ' ')
+ '</td>' + CASE WHEN slot = 37 THEN '</tr>' ELSE '' END
FROM SlotMatrix AS q LEFT OUTER JOIN dbo.EmpLeave AS el
ON q.Date = el.leave_date
AND el.EmployeeID = @EmployeeID
) -- now turn it sideways
SELECT m = '<!-- 00 -->',
[1] = '<tr><th>Month</th><th>S</th>', [2] = '<th>M</th>',
[3] = '<th>T</th>', [4] = '<th>W</th>', [5] = '<th>T</th>',
[6] = '<th>F</th>', [7] = '<th>S</th>', [8] = '<th>S</th>',
[9] = '<th>M</th>', [10] = '<th>T</th>', [11] = '<th>W</th>',
[12] = '<th>T</th>', [13] = '<th>F</th>', [14] = '<th>S</th>',
[15] = '<th>S</th>', [16] = '<th>M</th>', [17] = '<th>T</th>',
[18] = '<th>W</th>', [19] = '<th>T</th>', [20] = '<th>F</th>',
[21] = '<th>S</th>', [22] = '<th>S</th>', [23] = '<th>M</th>',
[24] = '<th>T</th>', [25] = '<th>W</th>', [26] = '<th>T</th>',
[27] = '<th>F</th>', [28] = '<th>S</th>', [29] = '<th>S</th>',
[30] = '<th>M</th>', [31] = '<th>T</th>', [32] = '<th>W</th>',
[33] = '<th>T</th>', [34] = '<th>F</th>', [35] = '<th>S</th>',
[36] = '<th>S</th>', [37] = '<th>M</th>'
UNION ALL
(
SELECT * FROM FinalHTML PIVOT (MAX(cell) FOR slot IN
(
[1], [2], [3], [4], [5], [6], [7], [8], [9], [10],[11],[12],[13],[14],
[15],[16],[17],[18],[19],[20],[21],[22],[23],[24],[25],[26],[27],[28],
[29],[30],[31],[32],[33],[34],[35],[36],[37]
)) AS p
)
ORDER BY m OPTION (MAXRECURSION 366);
END
GO
Hasil dari panggilan ini:
EXEC dbo.BuildLeaveHTMLTable @EmployeeID = 1;
Terlihat seperti ini (saya berhenti di kolom 7 hari):
Anda harus menambahkan <table>
/ </table>
wrapper sendiri, tetapi inilah hasilnya seperti ketika dimasukkan di antara mereka dan disimpan sebagai HTML (dan tentu saja Anda dapat lebih meningkatkannya dengan CSS):
Ketika cuti jatuh pada akhir pekan, warna cuti mengalahkan warna akhir pekan, tetapi itu mudah disesuaikan. Ubah ini:
+ COALESCE(' bgcolor=#' + RTRIM(el.leave_type_color),
CASE WHEN DATEPART(WEEKDAY, [Date]) IN (1,7)
THEN ' bgcolor=#cccccc' ELSE '' END)
Untuk ini:
+ CASE WHEN DATEPART(WEEKDAY, [Date]) IN (1,7)
THEN ' bgcolor=#cccccc' ELSE COALESCE(' bgcolor=#'
+ RTRIM(el.leave_type_color), '') END
Untuk mengonversi warna dalam format desimal (seperti 65280
) ke RGB-nya ( 00FF00
), Anda harus melakukan banyak manipulasi. Saya akan mempertimbangkan menyimpannya sebagai RGB hex di tempat pertama, tetapi saya memperbarui solusi di sini dengan sesuatu yang mirip dengan ini:
SELECT RIGHT(CONVERT(varchar(10),CONVERT(varbinary(8), 65280),1),6);