C# 有关生成复杂sql:sql Server的问题
我的情况是,我需要形成一个复杂的sql,以便在winform应用程序中显示与datagridview类似日历的界面。我已经完成了用表单编写代码的工作。现在我想用sql在数据库级别形成日历。这是我的UI看起来像 所以我要做的是……我首先生成一个数据表,并根据每年的月数添加列。在图片中有两个下拉列表,用户从中选择月份和年份 这样我就可以得到所选年份的月总天数C# 有关生成复杂sql:sql Server的问题,c#,sql-server,winforms,datagridview,C#,Sql Server,Winforms,Datagridview,我的情况是,我需要形成一个复杂的sql,以便在winform应用程序中显示与datagridview类似日历的界面。我已经完成了用表单编写代码的工作。现在我想用sql在数据库级别形成日历。这是我的UI看起来像 所以我要做的是……我首先生成一个数据表,并根据每年的月数添加列。在图片中有两个下拉列表,用户从中选择月份和年份 这样我就可以得到所选年份的月总天数 TotalDays = DateTime.DaysInMonth(int.Parse(ddlYear.Text), DateTime.Par
TotalDays = DateTime.DaysInMonth(int.Parse(ddlYear.Text), DateTime.ParseExact(ddlMonth.Text, "MMMM",
CultureInfo.InvariantCulture).Month);
现在,我将向datatable动态添加列,其中有两列是固定的,分别称为specialist id和name。下面是我首先用来填充数据表的代码
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.
接下来,我再次获取保存在另一个名为HourSheet
并重新填充日单元格,如下代码所示
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;
}
}
}
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();
}
}
}
}
}
strSql=“从小时表h,s中选择h.SpecialistID,s.Name,h.EntryDate,h.HoursData,h.Col,h.Row”;
strSql=strSql+“其中h.SpecialistID=s.SpecialistID和s.IsActive=1,s.IsSpecialist=1,s.IsExcluded=0”;
strSql=strSql+“和月(EntryDate)=“+DateTime.ParseExact(ddlMonth.Text,“MMMM”,CultureInfo.InvariantCulture)。月+”和年(EntryDate)=“+ddlYear.Text;
strSql=strSql+“由h.SpecialistID订购,日期(EntryDate)、月份(EntryDate)、年份(EntryDate)”;
ds=Common.GetDataSet(strSql,“”);
如果(ds.Tables.Count>0)
{
如果(ds.Tables[0].Rows.Count>0)
{
对于(int y=0;y,我首先建议您停止使用ANSI 89隐式连接语法,转而使用较新的ANSI 92显式连接语法。您使用的语法在20多年前就被替换了。进行切换的一些非常有说服力的原因是。因此,不要使用:
SELECT ...
FROM HourSheet h,Specialists s;
WHERE h.SpecialistID=s.SpecialistID
strSql = strSql + " and Year(EntryDate)=" + ddlYear.Text;
你应该:
SELECT ...
FROM HourSheet AS h
INNER JOIN Specialist AS s
ON s.SpecialistID = h.SpecialistID
第二,我坚持要求您开始使用参数化查询!尽管您的输入由下拉框控制,因此您实际上不易受到格式错误的sql或恶意sql注入的攻击,但由于您无法使用缓存的计划,因此每次都会强制重新编译查询。因此,请不要:
SELECT ...
FROM HourSheet h,Specialists s;
WHERE h.SpecialistID=s.SpecialistID
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);
然后,您需要做的就是修改函数Common.GetDataSet()
以接受SqlParameters,这样您就可以向数据库发送参数化查询
编辑
根据关于缺少数据的评论,您是对的,您需要从内部
切换到左连接
,但您还需要重新排列表和过滤器:
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;
这只需生成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;
我想连接应该保留在外部连接,因为小时表可能在某个时间没有根据条件提供的数据,但专家总是有数据。谢谢,但不能使用order by SpecialistID、Day(EntryDate)、Month(EntryDate)、Year(EntryDate)等order by子句“由于您只显示一个月的数据,按月份和年份排序毫无意义,它们都是一样的。同样,按天排序也不会有任何效果,因为您希望以列而不是行的形式进行排序。我已将专家ID添加到查询中,以便您能够按专家ID对数据透视结果进行排序。非常感谢您的帮助。一个小I假设我想在HoursData为空时显示8.00
,但如果当天是Sunday或Saturday
,那么我想显示S
。要更改的内容请指导。感谢我添加了第二次编辑,可以满足您的要求。问题是它需要混合数据类型,还需要将业务逻辑与数据混合。这可能会导致更好地在应用程序中处理,而不是在SQL中处理
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;