我处于这样的情况,我需要在winform应用程序中形成一个复杂的sql来显示与datagridview接口的日历。我已经完成了在表单中编写代码的工作。现在我想用sql在数据库级别形成日历。这是我的UI外观
所以我正在做什么....我首先生成一个数据表,并按照每年的蛾子数量添加列。在图片中,有两个用户选择月份和年份的下拉列表。
通过这种方式,我在选定年份的飞蛾中获得了完整的天数。TotalDays = DateTime.DaysInMonth(int.Parse(ddlYear.Text), DateTime.ParseExact(ddlMonth.Text, "MMMM",
CultureInfo.InvariantCulture).Month);
现在我正在动态地向datatable添加列,其中两列被修复,称为专家ID和名称。这是我用来首先填充数据表的代码。
DataTable dtHrs = new DataTable();
dtHrs.Columns.Add("SpecialistID");
dtHrs.Columns.Add("SpecialistName");
TotalDays = DateTime.DaysInMonth(int.Parse(ddlYear.Text), DateTime.ParseExact(ddlMonth.Text, "MMMM", CultureInfo.InvariantCulture).Month);
for (int i = 1; i <= TotalDays; i++)
{
dtHrs.Columns.Add(i.ToString());
}
ds = Common.GetDataSet("select distinct SpecialistID,Name from specialists Where IsActive=1 and IsSpecialist=1 and IsExcluded=0 order by SpecialistID", "");
if (ds.Tables.Count > 0)
{
if (ds.Tables[0].Rows.Count > 0)
{
for (int i = 0; i <= ds.Tables[0].Rows.Count - 1; i++)
{
dr = dtHrs.NewRow();
dr[0] = ds.Tables[0].Rows[i][0].ToString();
dr[1] = ds.Tables[0].Rows[i][1].ToString();
for (int y = 2; y <= dtHrs.Columns.Count - 1; y++)
{
dr[y] = "8.00";
}
dtHrs.Rows.Add(dr);
}
}
}
Common.GetDataSet() return data as per sql.
上面的代码会将专家ID和名称以及单元格值填充为默认"8.00"
通过以下代码
,在白天saturday and sunday
时将单元格颜色设置为红色
for (int y = 0; y <= dgView.Rows.Count - 1; y++)
{
for (int x = 2; x <= dtHrs.Columns.Count - 1; x++)
{
strDate = (dtHrs.Columns[x].ColumnName.ToString().Length > 1 ? dtHrs.Columns[x].ColumnName.ToString() : "0" + dtHrs.Columns[x].ColumnName.ToString()) + "/" + (DateTime.ParseExact(ddlMonth.Text, "MMMM", CultureInfo.InvariantCulture).Month > 9 ? DateTime.ParseExact(ddlMonth.Text, "MMMM", CultureInfo.InvariantCulture).Month.ToString() : "0" + DateTime.ParseExact(ddlMonth.Text, "MMMM", CultureInfo.InvariantCulture).Month.ToString()) + "/" + ddlYear.Text;
//IFormatProvider culture = new System.Globalization.CultureInfo("en-US", true);
strDayName = DateTime.ParseExact(strDate, "dd/MM/yyyy", CultureInfo.InvariantCulture).ToString("dddd");
if (strDayName.ToUpper() == "SATURDAY" || strDayName.ToUpper() == "SUNDAY")
{
dgView[x, y].Value = "S";
dgView.Rows[y].Cells[x].Style.BackColor = Color.Red;
}
}
}
接着我再次获取保存在名为HourSheet
的其他表格中的专家数据
并重新填充日期单元格,如下面的代码
strSql = "select h.SpecialistID,s.Name,h.EntryDate,h.HoursData,h.Col,h.Row from HourSheet h,Specialists s ";
strSql = strSql + "Where h.SpecialistID=s.SpecialistID and s.IsActive=1 and s.IsSpecialist=1 and s.IsExcluded=0";
strSql = strSql + " and Month(EntryDate)=" + DateTime.ParseExact(ddlMonth.Text, "MMMM", CultureInfo.InvariantCulture).Month + " and Year(EntryDate)=" + ddlYear.Text;
strSql = strSql + " order by h.SpecialistID,Day(EntryDate),Month(EntryDate),Year(EntryDate) ";
ds = Common.GetDataSet(strSql, "");
if (ds.Tables.Count > 0)
{
if (ds.Tables[0].Rows.Count > 0)
{
for (int y = 0; y <= dgView.Rows.Count - 1; y++)
{
for (int z = 0; z <= ds.Tables[0].Rows.Count - 1; z++)
{
if (dgView[1, y].Value.ToString().ToUpper() == ds.Tables[0].Rows[z]["Name"].ToString().ToUpper())
{
dgView[int.Parse(ds.Tables[0].Rows[z]["Col"].ToString()), y].Value = ds.Tables[0].Rows[z]["HoursData"].ToString();
}
}
}
}
}
这里我给出了我填充网格的所有c#代码,现在我被要求在数据库级别获取日历输出。我在db sql中不是很好。所以任何人都会讨论我需要在sql server store程序中编写的sql来获取上面图像的输出。感谢
答案 0 :(得分:2)
我首先建议您停止使用ANSI 89隐式连接语法,以支持更新的ANSI 92显式连接语法。您使用的语法已在20多年前被替换。进行切换的一些非常有说服力的理由是documented here。而不是:
SELECT ...
FROM HourSheet h,Specialists s;
WHERE h.SpecialistID=s.SpecialistID
你会:
SELECT ...
FROM HourSheet AS h
INNER JOIN Specialist AS s
ON s.SpecialistID = h.SpecialistID
我其次要坚持你开始使用参数化查询!即使您的输入是由下拉框控制的,因此您实际上并不容易受到格式错误的SQL或恶意SQL注入的影响,但由于您无法使用缓存计划,因此您仍然每次都强制重新编译查询。而不是:
strSql = strSql + " and Year(EntryDate)=" + ddlYear.Text;
你只需使用
strSql = strSql + " and Year(EntryDate) = @Year";
然后,您可以将参数添加到SQL命令中,如:
SqlCommand.Parameters.Add("@Year", SqlDbType.Int).Value = Int.Parse(ddlYear.Text);
更好的是,您可以避免在查询中调用EntryDate
上的函数,并获取查询的日期范围,因此请从下拉列表中获取日期/时间:
DateTime startDate = new DateTime(Int.Parse(ddlYear.Text, DateTime.ParseExact(ddlMonth.Text, "MMMM", CultureInfo.InvariantCulture).Month, 1);
然后将此日期传递给您的查询:
WHERE EntryDate >= @Date
AND EntryDate < DATEADD(MONTH, 1, @Date);
现在这会使您的查询sargable。
最后,在数据库中执行此操作,我只是让查询返回31天,然后简单地删除应用程序中的列。这比使用动态数量的列要容易得多。
要将数据转换为几天,我会使用PIVOT
函数,因此,通过上述更改,您的SQL将最终为:
WITH DataToPivot AS
( SELECT s.Name,
DayNumber = DATEPART(DAY, h.EntryDate),
h.HoursData
FROM HourSheet AS h
INNER JOIN Specialist AS s
ON s.SpecialistID = h.SpecialistID
WHERE h.EntryDate >= @Date
AND h.EntryDate < DATEADD(MONTH, 1, @Date)
AND s.IsActive = 1
AND s.IsSpecialist = 1
AND s.IsExcluded = 0
)
SELECT pvt.*
FROM DataToPivot AS d
PIVOT
( SUM(HoursData)
FOR DayNumber 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]
)
) AS pvt;
然后您需要做的就是修改函数Common.GetDataSet()
以接受SqlParameters,以便您可以将参数化查询发送到数据库。
修改强>
根据您对正确缺失数据的评论,您需要从INNER
切换到LEFT JOIN
,但您还需要重新排列表格和过滤器:
WITH DataToPivot AS
( SELECT s.SpecialistID,
s.Name,
DayNumber = DATEPART(DAY, h.EntryDate),
h.HoursData
FROM Specialist AS s
LEFT JOIN HourSheet AS h
ON h.SpecialistID = s.SpecialistID
AND h.EntryDate >= @Date
AND h.EntryDate < DATEADD(MONTH, 1, @Date)
WHERE s.IsActive = 1
AND s.IsSpecialist = 1
AND s.IsExcluded = 0
)
SELECT pvt.*
FROM DataToPivot AS d
PIVOT
( SUM(HoursData)
FOR DayNumber 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]
)
) AS pvt
ORDER BY pvt.SpecialistID;
编辑2
SET DATEFIRST 1;
WITH DataToPivot AS
( SELECT s.SpecialistID,
s.Name,
DayNumber = DATEPART(DAY, h.EntryDate),
HoursData = CASE WHEN DATEPART(WEEKDAY, h.EntryDate) IN (6, 7) THEN CONVERT(VARCHAR(10), 'S')
WHEN h.HoursData IS NULL THEN '8.00'
ELSE h.HoursData
END
FROM Specialist AS s
LEFT JOIN HourSheet AS h
ON h.SpecialistID = s.SpecialistID
AND h.EntryDate >= @Date
AND h.EntryDate < DATEADD(MONTH, 1, @Date)
WHERE s.IsActive = 1
AND s.IsSpecialist = 1
AND s.IsExcluded = 0
)
SELECT pvt.SpecialistID,
pvt.Name,
[1] = ISNULL(pvt.[1], '8.00'),
[2] = ISNULL(pvt.[2], '8.00'),
[3] = ISNULL(pvt.[3], '8.00'),
[4] = ISNULL(pvt.[4], '8.00'),
[5] = ISNULL(pvt.[5], '8.00'),
[6] = ISNULL(pvt.[6], '8.00'),
[7] = ISNULL(pvt.[7], '8.00'),
[8] = ISNULL(pvt.[8], '8.00'),
[9] = ISNULL(pvt.[9], '8.00'),
[10] = ISNULL(pvt.[10], '8.00'),
[11] = ISNULL(pvt.[11], '8.00'),
[12] = ISNULL(pvt.[12], '8.00'),
[13] = ISNULL(pvt.[13], '8.00'),
[14] = ISNULL(pvt.[14], '8.00'),
[15] = ISNULL(pvt.[15], '8.00'),
[16] = ISNULL(pvt.[16], '8.00'),
[17] = ISNULL(pvt.[17], '8.00'),
[18] = ISNULL(pvt.[18], '8.00'),
[19] = ISNULL(pvt.[19], '8.00'),
[20] = ISNULL(pvt.[20], '8.00'),
[21] = ISNULL(pvt.[21], '8.00'),
[22] = ISNULL(pvt.[22], '8.00'),
[23] = ISNULL(pvt.[23], '8.00'),
[24] = ISNULL(pvt.[24], '8.00'),
[25] = ISNULL(pvt.[25], '8.00'),
[26] = ISNULL(pvt.[26], '8.00'),
[27] = ISNULL(pvt.[27], '8.00'),
[28] = ISNULL(pvt.[28], '8.00'),
[29] = ISNULL(pvt.[29], '8.00'),
[30] = ISNULL(pvt.[30], '8.00'),
[31] = ISNULL(pvt.[31], '8.00')
FROM DataToPivot AS d
PIVOT
( SUM(HoursData)
FOR DayNumber 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]
)
) AS pvt
ORDER BY pvt.SpecialistID;
编辑3
为了生成HourSheet中不存在的数据(例如,对于周末,您需要生成一个月的日期列表,然后您可以保持联接到此。要生成列表,我只是使用一个表值构造:
SELECT N = ROW_NUMBER() OVER(ORDER BY t1.N) - 1
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1)) t1 (N)
CROSS JOIN (VALUES (1),(1),(1),(1)) t2 (N)
这只生成32行(8x4),然后使用RowNumber为每行增加一个递增的数字。可以将此数字添加到您的开始日期以获取日期列表,最后您可以使用(DATEDIFF(DAY, @Date, DATEADD(MONTH, 1, @Date)))
计算当月的天数,并使用TOP
仅返回此行数:
DECLARE @Date DATE = '20150201';
SELECT TOP (DATEDIFF(DAY, @Date, DATEADD(MONTH, 1, @Date)))
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY T1.n) - 1, @Date)
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1)) t1 (N)
CROSS JOIN (VALUES (1),(1),(1),(1)) t2 (N);
所以你的最终查询是:
WITH Dates AS
( SELECT TOP (DATEDIFF(DAY, @Date, DATEADD(MONTH, 1, @Date)))
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY T1.n) - 1, @Date)
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1)) t1 (N)
CROSS JOIN (VALUES (1),(1),(1),(1)) t2 (N);
), DataToPivot AS
( SELECT s.SpecialistID,
s.Name,
DayNumber = DATEPART(DAY, d.Date),
HoursData = CASE WHEN DATEPART(WEEKDAY, d.Date) IN (6, 7) THEN CONVERT(VARCHAR(10), 'S')
WHEN h.HoursData IS NULL THEN '8.00'
ELSE h.HoursData
END
FROM Dates AS d
CROSS JOIN Specialist AS s
LEFT JOIN HourSheet AS h
ON h.SpecialistID = s.SpecialistID
AND CAST(h.EntryDate AS DATE) = d.Date
WHERE s.IsActive = 1
AND s.IsSpecialist = 1
AND s.IsExcluded = 0
)
SELECT pvt.SpecialistID,
pvt.Name,
[1] = ISNULL(pvt.[1], '8.00'),
[2] = ISNULL(pvt.[2], '8.00'),
[3] = ISNULL(pvt.[3], '8.00'),
[4] = ISNULL(pvt.[4], '8.00'),
[5] = ISNULL(pvt.[5], '8.00'),
[6] = ISNULL(pvt.[6], '8.00'),
[7] = ISNULL(pvt.[7], '8.00'),
[8] = ISNULL(pvt.[8], '8.00'),
[9] = ISNULL(pvt.[9], '8.00'),
[10] = ISNULL(pvt.[10], '8.00'),
[11] = ISNULL(pvt.[11], '8.00'),
[12] = ISNULL(pvt.[12], '8.00'),
[13] = ISNULL(pvt.[13], '8.00'),
[14] = ISNULL(pvt.[14], '8.00'),
[15] = ISNULL(pvt.[15], '8.00'),
[16] = ISNULL(pvt.[16], '8.00'),
[17] = ISNULL(pvt.[17], '8.00'),
[18] = ISNULL(pvt.[18], '8.00'),
[19] = ISNULL(pvt.[19], '8.00'),
[20] = ISNULL(pvt.[20], '8.00'),
[21] = ISNULL(pvt.[21], '8.00'),
[22] = ISNULL(pvt.[22], '8.00'),
[23] = ISNULL(pvt.[23], '8.00'),
[24] = ISNULL(pvt.[24], '8.00'),
[25] = ISNULL(pvt.[25], '8.00'),
[26] = ISNULL(pvt.[26], '8.00'),
[27] = ISNULL(pvt.[27], '8.00'),
[28] = ISNULL(pvt.[28], '8.00'),
[29] = ISNULL(pvt.[29], '8.00'),
[30] = ISNULL(pvt.[30], '8.00'),
[31] = ISNULL(pvt.[31], '8.00')
FROM DataToPivot AS d
PIVOT
( SUM(HoursData)
FOR DayNumber 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]
)
) AS pvt
ORDER BY pvt.SpecialistID;