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

时间:2015-09-01 12:29:09

标签: c# sql-server winforms datagridview

我处于这样的情况,我需要在winform应用程序中形成一个复杂的sql来显示与datagridview接口的日历。我已经完成了在表单中编写代码的工作。现在我想用sql在数据库级别形成日历。这是我的UI外观 enter image description here

所以我正在做什么....我首先生成一个数据表,并按照每年的蛾子数量添加列。在图片中,有两个用户选择月份和年份的下拉列表。

通过这种方式,我在选定年份的飞蛾中获得了完整的天数。

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来获取上面图像的输出。感谢

1 个答案:

答案 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;