【问题标题】:How to fill a datagridview with data depending on the value in a database column如何根据数据库列中的值用数据填充 datagridview
【发布时间】:2018-04-25 13:56:31
【问题描述】:

假设我有一个如下的数据库表“USAGE”:

MACHINE     WEEK        HOURS       PIECES
A           2018-12     1           2
B           2018-12     3           4
A           2018-13     12          1
B           2018-13     2           5
A           2018-15     6           6
B           2018-15     2           2
C           2018-16     2           1
D           2018-17     4           22
B           2018-17     3           9
A           2018-18     1           8
E           2018-18     4           4
D           2018-13     2           4

我想在datagridview中显示数据如下:

MACHINE     2018-12     2018-13     2018-14     2018-15     2018-16     2018-17     2018-18
A           1H 2P       12H 1P                  6H 6P                               1H 8P
B           3H 4P       2H 5P                   2H 2P                   3H 9P
C                                                           2H 1P
D                       2H 4P                                           4H 22P
E                                                                                   4H 4P

在视觉 foxpro 中,这非常简单。

this.edType      = 1
this.ehCols      = 20
this.ehStartval  = date()+ 7

这在标题中创建了 20 个不同周的列。然后使用如下命令完成:

this.ehKeyFld    = "iif(usage.hours > 0 and usage.pieces > 0,STR(usage.hours,4) + 'H ' + STR(usage.pieces,3)+'P','')"

现在我想做同样的事情,但我喜欢在 datagridview 中显示数据。

如果我这样做:

select machine, iif(week='2018-12',str(hours,4) + 'H' + str(pieces,3) + 'P','') as '2018-12', IIF(week='2018-13',str(hours,4) + 'H' + str(pieces,3) + 'P','') as '2018-13', IIF(week='2018-14',str(hours,4) + 'H' + str(pieces,3) + 'P','') as '2018-14', IIF(week='2018-15',str(hours,4) + 'H' + str(pieces,3) + 'P','') as '2018-15', IIF(week='2018-16',str(hours,4) + 'H' + str(pieces,3) + 'P','') as '2018-16', IIF(week='2018-17',str(hours,4) + 'H' + str(pieces,3) + 'P','') as '2018-17', IIF(week='2018-18',str(hours,4) + 'H' + str(pieces,3) + 'P','') as '2018-18' from GCCTEST.dbo.usage

我得到以下输出:

machine 2018-12 2018-13 2018-14 2018-15 2018-16 2018-17 2018-18
A       1H  2P                      
B       3H  4P                      
A               12H  1P                 
B               2H  5P                  
A                               6H  6P          
B                               2H  2P          
C                                       2H  1P      
D                                               4H 22P  
B                                               3H  9P  
A                                                       1H  8P
E                                                       4H  4P
D               2H  4P                  

我正在寻找一个 SQL 中的命令,它可以按照我想要的方式填充 datagridview,即每台机器的一行数据都在正确的列中。

【问题讨论】:

    标签: sql datagridview visual-foxpro


    【解决方案1】:

    我接受 VFP 让事情变得更容易。但是,说到 datagridview,我会假设您的意思是 C#,这也可以在 C# 中以多种方式完成。使用 SQL,您还可以使用“PIVOT”。即:

    DECLARE @table TABLE([MACHINE] VARCHAR(1), [WEEK] VARCHAR(7), [HOURS] INT, [PIECES] INT);
    
    INSERT INTO @table(MACHINE, WEEK, HOURS, PIECES)
    VALUES('A', '2018-12', 1, 2),
        ('B', '2018-12', 3, 4),
        ('A', '2018-13', 12, 1),
        ('B', '2018-13', 2, 5),
        ('A', '2018-15', 6, 6),
        ('B', '2018-15', 2, 2),
        ('C', '2018-16', 2, 1),
        ('D', '2018-17', 4, 22),
        ('B', '2018-17', 3, 9),
        ('A', '2018-18', 1, 8),
        ('E', '2018-18', 4, 4),
        ('D', '2018-13', 2, 4);
    
    SELECT *
    FROM(
        SELECT MACHINE, WEEK, STR(HOURS, 4)+'H'+STR(PIECES, 3)+'P' AS data
        FROM @table
        ) AS source
    PIVOT(
         MAX(data)
         FOR WEEK IN([2018-12], [2018-13], [2018-14], [2018-15], [2018-16], [2018-17], [2018-18])
         ) AS mypivot;
    

    但是,您不希望在 SQL 中对所有这些列进行硬编码。检查“动态 PIVOT”。以下是 Northwind 数据的示例:

    DECLARE @COUNTRY NVARCHAR(MAX) = '' , @cTotal NVARCHAR(MAX) = ''
    
    SELECT  @COUNTRY = @COUNTRY + COALESCE(QUOTENAME(Country) + ', ', '')
    FROM    Customers
    WHERE   EXISTS ( SELECT *
                     FROM   [Orders] AS [o]
                     WHERE  o.[CustomerID] = Customers.[CustomerID] )
    GROUP BY Country;
    
    SET @COUNTRY = LEFT(@COUNTRY, LEN(@COUNTRY) - 1);
    
    SELECT  @cTotal = @cTotal + COALESCE('Coalesce('+QUOTENAME(Country) + ',0) +', '')
    FROM    Customers
    WHERE   EXISTS ( SELECT *
                     FROM   [Orders] AS [o]
                     WHERE  o.[CustomerID] = Customers.[CustomerID] )
    GROUP BY Country;
    
    SET @cTotal = LEFT(@cTotal, LEN(@cTotal) - 1);
    
    DECLARE @SQL NVARCHAR(MAX);
    
    SET @SQL = 'SELECT * , ' + @cTotal + ' AS TOTAL FROM 
        (
            SELECT  oe.EmployeeID, oe.LastName, oe.ShipCountry AS CO,
                    OD.Quantity * OD.UnitPrice AS QU
            FROM    (
                      SELECT  EmployeeID, LastName, ShipCountry
                      FROM    (
                                SELECT DISTINCT
                                        ShipCountry
                                FROM    Orders
                              ) o ,
                              Employees
                    ) oe
            LEFT JOIN Orders O ON O.EmployeeID = oe.EmployeeID AND
                                  [oe].[ShipCountry] = [O].[ShipCountry]
            LEFT JOIN [Order Details] OD ON OD.OrderID = O.OrderID 
        ) AS T
        PIVOT(SUM(QU) FOR CO IN (' + @COUNTRY + ')) AS PVT
        ORDER BY 1';
    
    EXEC(@SQL); 
    

    【讨论】:

    • 在 VB.NET 中的 Datagridview。看着这个例子,我意识到我对 sql 了解不多。我将尝试一下,但如果有办法在 VB.NET 中进行,那将给我另一种选择。示例:link
    【解决方案2】:

    我的解决方案: 我在 tblMachines 中有一个包含机器描述的数据库,在 tblUsage 中有一个包含机器使用情况的数据库。 在我的 windows 窗体上,我有一个 datagridview Dgv_4 和一个 Combobox1

    ' Separate class to do the sql command
    Public SQL As New SQLControl
    Private Dgv_4Query As String = $"m.id,m.listorder,m.prio,m.machine as 'Machine',m.weekuren as 'Hour/wk', week, STR(u.HOURS, len(u.HOURS))+'h_'+STR(u.PIECES, len(u.PIECES))+'p' as data FROM {GV.DB}tblMachines m "
    Private Dgv_4Join As String = $"LEFT JOIN {GV.DB}tblUsage u on m.machine = u.machine "
    Private Dgv_4Pivot As String = ""
    Private EmptyQueryStringDgv_4 As String = ""
    Private TotNumWeeks As Integer = 15
    
    Private Sub DataGridViewForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        MdiParent = Main
        ' Create headers for datagridview with dates and weeknumbers starting with current week
        Dim WeekNumber As Integer = DatePart(DateInterval.WeekOfYear, Date.Now, FirstDayOfWeek.Monday, FirstWeekOfYear.System)
        ' First part of the pivot command
        Dim PivotString As String = $"PIVOT (MAX(data) FOR week IN("
        Dim endline As String = ","
        ' Middle part of the pivot command
        For i As Integer = 0 To TotNumWeeks
            If i = TotNumWeeks Then
                ' Last part of the pivot command
                endline = ")) as mypivot"
            End If
            PivotString = PivotString + $"[{Date.Today.Year}-{WeekNumber + i}]{endline}"
        Next
        Dgv_4Pivot = PivotString
        EmptyQueryStringDgv_4 = $"SELECT * FROM (SELECT {Dgv_4Query}{Dgv_4Join}) as source {Dgv_4Pivot} order by prio"
        LoadDataGridView1()
        ' Load combobox with current week
        ComboBox1.Items.Clear()
        ComboBox1.Text = $"{Date.Today.Year}-{WeekNumber}"
    End Sub
    
    Public Sub LoadDataGridView1(Optional ByVal QueryString As String = "", Optional ByVal Parameter As String = "")
        Dim dgvclass As New DataGridViewClass()
        Dim dname As DataGridView = Dgv_4
        Dim SelectRow As Integer = 0
        If QueryString = "" Then
            ' Default query
            QueryString = EmptyQueryStringDgv_4
        End If
        ' Use the sql command to request the data
        dgvclass.FillDataGridView(QueryString, Parameter, "Normal") = dname
        ' Hide column(s)
        dname.Columns(0).Visible = False
        dname.Columns(1).Visible = False
        ResizeGrid.ResizeGrid(dname, dname.Width)
    End Sub
    

    这将显示并加载从当前周开始的数据网格视图,并且您可以使用组合框选择不同的周:

    Private Sub ComboBox_Drop(sender As Object, e As EventArgs) Handles ComboBox1.DropDown
        Dim ac As ComboBox = DirectCast(sender, ComboBox)
        If Me.ActiveControl Is ComboBox1 Then
            ComboBox1.Items.Clear()
            ' Fill combobox with a range of weeks to chose from
            For i As Integer = -1 To 1 Step 1
                For j As Integer = 1 To 52
                    Dim num As String = j.ToString
                    If Len(num) < 2 Then
                        num = $"0{num}"
                    End If
                    ComboBox1.Items.Add(Date.Today.Year + i & "-" & num)
                Next
            Next
        End If
    End Sub
    
    Private Sub ComboBox_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
        ' Create headers for datagridview with dates and weeknumbers
        Dim result As String = ComboBox1.SelectedItem
        Dim PivotString As String = $"PIVOT (MAX(data) FOR week IN("
        Dim endline As String = ","
        Dim weekadd As Integer = 0
        For i As Integer = 0 To TotNumWeeks
            If i = TotNumWeeks Then
                ' Close string
                endline = ")) as mypivot"
            Else
                ' Check year limits
                If (Convert.ToInt32(Strings.Right(result, 2)) + i) > 52 Then
                    ' Change year and week
                    weekadd = 0
                    result = $"{Convert.ToInt32(Strings.Left(result, 4)) + 1.ToString}-01"
                End If
            End If
            PivotString = PivotString + $"[{Strings.Left(result, 4)}-{(Convert.ToInt32(Strings.Right(result, 2)) + weekadd).ToString("D2")}]{endline}"
            weekadd = weekadd + 1
        Next
        Dgv_4Pivot = PivotString
        ' Run query without pivot, seems to prevent insert columns on the right
        LoadDataGridView1($"SELECT {Dgv_4Query}{Dgv_4Join}")
        ' Run query with pivot to fill datagridview with new weeks
        LoadDataGridView1($"SELECT * FROM (SELECT {Dgv_4Query}{Dgv_4Join}) as source {Dgv_4Pivot} order by prio", "")
    End Sub
    

    输出示例:https://imgur.com/a/DTtloNz

    【讨论】:

    • 好的。可能你错过了我给出的 VB 代码。它不需要在 SQL Server 中进行透视(在 SQL Server 中进行透视并没有真正增加任何好处)。恕我直言,这样做客户端更容易,代码更易于维护。
    • 是的,我确实错过了那个 VB 代码。我认为这是一种更好的方法,没有 sql server 中的枢轴和客户端编码。我会调查一下。
    【解决方案3】:

    我也发现在 SQL 中做起来更困难,而且 .Net 缺乏 VFP 的活力 你仍然可以通过自己的一些自定义数据透视来解决这个问题。在 VFP 中,枢轴通常称为交叉表。实际上,它是关于汇总数据,然后分发到列,其中一列值(这里是机器)被转置到列。在您的示例中,数据已经汇总,实际上不需要使用 MS SQL 的数据透视表。恕我直言,可以获取该数据并在数据表中本地创建交叉表,因为您只有大约 20-25 列(周)并且最多可能有 1000 台机器。这是采样代码(代码是使用 Telerik 代码转换器从 C# 转换而来的 - 注意:我昨天使用 MS SQL 服务器基础查询只是为了模拟您的数据将来自 MS SQL):

    Private Sub Main()
        Dim sql As String = "DECLARE @table TABLE([MACHINE] VARCHAR(1), [WEEK] VARCHAR(7), [HOURS] INT, [PIECES] INT);
    
    INSERT INTO @table(MACHINE, WEEK, HOURS, PIECES)
    VALUES('A', '2018-12', 1, 2),
        ('B', '2018-12', 3, 4),
        ('A', '2018-13', 12, 1),
        ('B', '2018-13', 2, 5),
        ('A', '2018-15', 6, 6),
        ('B', '2018-15', 2, 2),
        ('C', '2018-16', 2, 1),
        ('D', '2018-17', 4, 22),
        ('B', '2018-17', 3, 9),
        ('A', '2018-18', 1, 8),
        ('E', '2018-18', 4, 4),
        ('D', '2018-13', 2, 4);
    
        SELECT MACHINE, WEEK, HOURS, PIECES FROM @table"
        Dim tbl As DataTable = New DataTable()
        Using con = New SqlConnection("server=.\SQLExpress;Trusted_Connection=yes")
            con.Open()
            tbl.Load(New SqlCommand(sql, con).ExecuteReader())
            con.Close()
        End Using
    
        Dim weeks = 20
        Dim weekStart = 12
        Dim year = 2018
        Dim result As DataTable = New DataTable()
        result.Columns.Add("Machine", GetType(String))
        For i As Integer = 0 To weeks - 1
            Dim week = $"{year}-{i + weekStart}"
            result.Columns.Add(week, GetType(String))
        Next
    
        Dim lst = tbl.AsEnumerable()
        Dim machines = lst.[Select](Function(l) l.Field(Of String)("Machine")).Distinct().OrderBy(Function(l) l)
        For Each machine As String In machines
            Dim row = result.NewRow()
            row("Machine") = machine
            For i As Integer = 0 To weeks - 1
                Dim week = $"{year}-{i + weekStart}"
                Dim weekValue = lst.SingleOrDefault(Function(t) t.Field(Of String)("WEEK") = week AndAlso t.Field(Of String)("Machine") = machine)
                row(week) = If(weekValue Is Nothing, "", $"{CInt(weekValue("Hours"))}H {CInt(weekValue("Pieces"))}P")
            Next
    
            result.Rows.Add(row)
        Next
    
        Dim f As Form = New Form()
        f.Controls.Add(New DataGridView With {.Dock = DockStyle.Fill, .DataSource = result})
        f.Show()
    End Sub
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-06-20
      • 2023-03-14
      • 1970-01-01
      • 1970-01-01
      • 2021-05-19
      • 2014-01-27
      • 1970-01-01
      • 2016-11-25
      相关资源
      最近更新 更多