C# 有关生成复杂sql:sql Server的问题

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

我的情况是,我需要形成一个复杂的sql,以便在winform应用程序中显示与datagridview类似日历的界面。我已经完成了用表单编写代码的工作。现在我想用sql在数据库级别形成日历。这是我的UI看起来像

所以我要做的是……我首先生成一个数据表,并根据每年的月数添加列。在图片中有两个下拉列表,用户从中选择月份和年份

这样我就可以得到所选年份的月总天数

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;