【问题标题】:VB Reading data from SQL Server to Array, writing into .CSVVB 从 SQL Server 读取数据到数组,写入 .CSV
【发布时间】:2020-01-29 15:56:46
【问题描述】:

我正在尝试将 SQL Server 中的数据读取到数组中。之后,我想将每一行写入单独的 .csv 文件。

这是我目前的代码:

Imports System.Data.SqlClient

Public Class Form1
    Public SQLcn As New SqlConnection

    Public Function Connect() As Boolean
        SQLcn = New SqlConnection("Server = Server01;Database=PROD;User ID=user; Password = 123")

        Try
            SQLcn.Open()

            Return True

        Catch ex As Exception

            MessageBox.Show(ex.Message)
            Return False

        End Try
    End Function

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim SQLQuery As String
        Dim SQLcmd As SqlCommand
        Dim SQLrdr As SqlDataReader
        Dim NAVArray As New ArrayList()
        Call Connect()

        SQLQuery = "SELECT No_ FROM " & "dbo.Database" & " Where No_ LIKE '10007*'"

        SQLcmd = New SqlCommand(SQLQuery, SQLcn)
        SQLrdr = SQLcmd.ExecuteReader()

        While SQLrdr.Read()

            Dim dict As New Dictionary(Of String, Object)
            For count As Integer = 0 To (SQLrdr.FieldCount - 1)
                dict.Add(SQLrdr.GetName(count), SQLrdr(count))
            Next

            NAVArray.Add(dict)

        End While

        ExportCSV(NAVArray, "\\path\path\path")

        SQLcn.Close()

    End Sub

    Function ExportCSV(ByVal Daten As ArrayList, ByVal Pfad As String) As Boolean
        Dim Nummer As Integer

        For Each Nummer In Daten
            Dim csv As IO.StreamWriter = My.Computer.FileSystem.OpenTextFileWriter(Pfad & Nummer, False)

            csv.WriteLine("formatcount;formatname;printername;Beschreibung;")
            csv.WriteLine("1;\\path\path\path\Format1.fmt;")
            csv.Close()

        Next

        Return 0
    End Function

End Class

NAVArray 甚至没有被数据填充。

另外,我不知道如何将数据写入CSV.Writeline

编辑:

新代码(目前有效)如下所示:

Option Infer On
Option Strict On

Imports System.Data.SqlClient

Imports System.IO

Public Class Form1
    Public SQLcn As New SqlConnection

    Public Function GetData(databaseColumnNames As String()) As DataTable

        Dim dt As New DataTable()

        Dim csb As New SqlConnectionStringBuilder With {.DataSource = "NAVDB01",
                                                        .InitialCatalog = "NAV110_PROD",
                                                        .UserID = "paz",
                                                        .Password = "****"
                                                        }

        Dim columnNames = " " & String.Join(", ", databaseColumnNames.Select(Function(c) "[" & c & "]")) & " "

        Dim sql = "SELECT " & columnNames & " FROM [dbo.Part1 Part2$Item] WHERE No_ LIKE '10007%'"

        Using conn = New SqlConnection(csb.ConnectionString),
                cmd = New SqlCommand(sql, conn)

            Dim DAdap As New SqlDataAdapter(cmd)
            DAdap.Fill(dt)

        End Using

        Return dt


    End Function

    Function CsvLine(a As Object(), separator As Char) As String

        Dim b = a.Select(Function(x) x.ToString()).ToArray()

        For i = 0 To b.Count - 1

            If b(i).IndexOfAny({separator, Chr(42), Chr(10), Chr(13)}) >= 0 Then
                b(i) = b(i).Replace("""", """""")
                b(i) = """" & b(i) & """"
            End If
        Next

        Return String.Join(separator, b)

    End Function

    Sub WriteCsvFiles(destPath As String, headings As String(), dt As DataTable)
        Dim separator As Char = ";"c
        Dim header = String.Join(separator, headings)

        For Each r As DataRow In dt.Rows

            Dim destFile = Path.Combine(destPath, r(0).ToString().Trim() & ".csv")

            Using sw As New StreamWriter(destFile)
                sw.WriteLine(header)
                sw.WriteLine(CsvLine(r.ItemArray, separator))
            End Using

        Next

    End Sub
    Private Sub bnDatenVerarbeiten_Click() Handles bnDatenVerarbeiten.Click
        Dim destinationFolder = "\\fileserver02\Folder1"


        Dim columnsToUseSQL = {"Description"}
        Dim columnsToUseCSV = {"formatcount", "formatname", "printername", "Beschreibung"}
        Dim daten = GetData(columnsToUseSQL)


        WriteCsvFiles(destinationFolder, columnsToUseCSV, daten)


    End Sub
End Class

关于附加输入。 将实施 5​​ 个字段: Formatcount 作为 InputBox 格式名称为下拉 printername As Dropown(由 Formatname 定义的默认值) itemnumber 作为输入框(用作 SQL 的过滤器) 债权人作为输入框(用于第二条 SQL 语句)

直接进入 csv 的字段。 : 格式计数 格式名 打印机名称

结果到 csv 的字段。 : itemnumber(结果,例如“描述”) 债权人 (result = "specialcode";"countrycode")

EDIT2:

当前状态:

Option Infer On
Option Strict On

Imports System.Data.SqlClient
Imports System.IO

Public Class Form1
    Public SQLcn As New SqlConnection

    Public Function GetData(databaseColumnNames As String()) As DataTable

        Dim dt As New DataTable()

        Dim csb As New SqlConnectionStringBuilder With {.DataSource = "Server01",
                                                        .InitialCatalog = "NAV110_PROD",
                                                        .UserID = "paz",
                                                        .Password = "***"
                                                        }

        Dim columnNames = " " & String.Join(", ", databaseColumnNames.Select(Function(c) "[" & c & "]")) & " "

        Dim sql = "SELECT " & columnNames & " FROM [dbo.Part1 Part2$Item] WHERE No_ LIKE '10007%'"

        Using conn = New SqlConnection(csb.ConnectionString),
                cmd = New SqlCommand(sql, conn)

            Dim DAdap As New SqlDataAdapter(cmd)
            DAdap.Fill(dt)

        End Using

        Return dt


    End Function

    Function CsvLine(a As Object(), separator As Char) As String

        Dim b = a.Select(Function(x) x.ToString()).ToArray()

        For i = 0 To b.Count - 1

            If b(i).IndexOfAny({separator, Chr(42), Chr(10), Chr(13)}) >= 0 Then
                b(i) = b(i).Replace("""", """""")
                b(i) = """" & b(i) & """"
            End If
        Next

        Return String.Join(separator, b)

    End Function

    Sub WriteCsvFiles(destPath As String, headings As String(), dt As DataTable)
        Dim separator As Char = ";"c
        Dim header = String.Join(separator, headings)

        For Each r As DataRow In dt.Rows
            Dim destFile = Path.Combine(destPath, r(0).ToString().Trim() & ".csv")

            Using sw As New StreamWriter(destFile)
                sw.WriteLine(header)
                sw.WriteLine(CsvLine(r.ItemArray, separator))
            End Using

        Next

    End Sub
    Private Sub bnDatenVerarbeiten_Click() Handles bnDatenVerarbeiten.Click
        Dim destinationFolder = "\\fileserver02\folder"
        Dim Anzahl = 1
        Dim Format = "\\fileserver02\folder2"
        Dim Drucker = "\\PRNSRV\Druckdruck"


        Dim columnsToUseSQL = {"Description", "Description 2"}
        Dim columnsToUseCSV = {"Beschreibung", "Beschreibung 2", "formatcount", "formatname", "printername"}
        Dim daten = GetData(columnsToUseSQL)

        daten.Columns.Add("formatcount", GetType(Integer))
        daten.Columns.Add("formatname", GetType(String))
        daten.Columns.Add("printername", GetType(String))

        daten.Rows.Add(daten.Rows(0).Item("Description"), daten.Rows(0).Item("Description 2"), Anzahl, Format, Drucker)


        WriteCsvFiles(destinationFolder, columnsToUseCSV, daten)


    End Sub

End Class

【问题讨论】:

  • 您确定查询返回任何内容吗?你真的有一个名为Database 的表吗?你测试过查询吗?连接字符串来创建 SQL 查询是一个非常糟糕的主意,但这次它什么也没做,你可以只写 "SELECT No_ FROM dbo.Database Where No_ LIKE '10007*'"
  • SQL Server 中的通配符是%,表示多个字符,_ 表示单个字符:LIKE (Transact-SQL) documentation
  • For Each Nummer In Daten 字典的 ArrayList 怎么会有整数?
  • 这个GetData() 方法将迫使您以一种极易受到SQL 注入问题攻击的方式构建SQL 查询。

标签: .net sql-server vb.net


【解决方案1】:

您似乎想从数据库中选择 4 列,所以为了回答这个问题,我用这些数据制作了一个名为“Huber”的表:

No_ formatcount formatname  printername Beschreibung
10007       1           Hello   World   Starting "entry".
100071      2           Yellow  Flower  NULL
100072      3           Grey    Rock    Let's have a ; here.

您可能需要灵活选择哪些列,因此我将列名设为数组。

CSV 文件有一个标准:RFC 4180,所以我们不妨尝试遵循它。

在我看来,在这种情况下,将数据检索到 DataTable 中是最简单的。由于每个数据都会转换成String写入CSV文件,这样数据库中的NULL数据不会有问题。

使用数据库时,最好是打开连接,做操作,处理掉连接。 VB.NET Using 语句会自动处理 dispose,即使出现问题。 DataAdapter.Fill 方法为您打开和关闭(实际上它使连接保持与调用之前相同的状态)。不要在问题中使用“Function Connect()”之类的东西:这会导致问题。

鉴于所有这些,我使用了以下代码:

Option Infer On
Option Strict On

Imports System.Data.SqlClient
Imports System.IO

Public Class Form1

    Function GetData(databaseColumnNames As String()) As DataTable
        Dim dt As New DataTable()

        Dim csb As New SqlConnectionStringBuilder With {.DataSource = "Server01",
                                                        .InitialCatalog = "PROD",
                                                        .UserID = "user",
                                                        .Password = "123"}

        ' Put the column names in square brackets in case a reserved word is used as a column name.
        ' Does not take into account using square brackets in a column name (don't do that).
        Dim columnNames = " " & String.Join(", ", databaseColumnNames.Select(Function(c) "[" & c & "]")) & " "

        Dim sql = "SELECT " & columnNames & " FROM dbo.[Huber] WHERE [No_] LIKE '10007%'"

        Using conn = New SqlConnection(csb.ConnectionString),
                cmd = New SqlCommand(sql, conn)

            Dim da As New SqlDataAdapter(cmd)
            da.Fill(dt)

        End Using

        Return dt

    End Function

    Function CsvLine(a As Object(), separator As Char) As String
        ' Ref: RFC 4180 "Common Format and MIME Type for Comma-Separated Values (CSV) Files"
        ' https://www.rfc-editor.org/rfc/rfc4180

        Dim b = a.Select(Function(x) x.ToString()).ToArray()

        For i = 0 To b.Count - 1
            ' If the field contains the separator, a double-quote, LF, or CR, then make adjustments:
            If b(i).IndexOfAny({separator, Chr(42), Chr(10), Chr(13)}) >= 0 Then
                b(i) = b(i).Replace("""", """""")
                b(i) = """" & b(i) & """"
            End If
        Next

        Return String.Join(separator, b)

    End Function

    Sub WriteCsvFiles(destPath As String, headings As String(), dt As DataTable)
        Dim separator As Char = ";"c
        Dim header = String.Join(separator, headings)

        For Each r As DataRow In dt.Rows
            ' Use the first column for the filename:
            Dim destFile = Path.Combine(destPath, r(0).ToString().Trim() & ".csv")

            Using sw As New StreamWriter(destFile)
                sw.WriteLine(header)
                sw.WriteLine(CsvLine(r.ItemArray, separator))
            End Using

        Next

    End Sub

    Private Sub bnDatenVerarbeiten_Click(sender As Object, e As EventArgs) Handles bnDatenVerarbeiten.Click
        Dim destinationFolder = "C:\Temp\Huber"
        Directory.CreateDirectory(destinationFolder)

        ' The names of the columns in the database...
        Dim columnsToUse = {"formatcount", "formatname", "printername", "Beschreibung"}
        Dim daten = GetData(columnsToUse)

        ' You could use different text for the headers if required.
        WriteCsvFiles(destinationFolder, columnsToUse, daten)

    End Sub

End Class

获取 3 个文件:

1.csv

formatcount;formatname;printername;Beschreibung
1         ;Hello;World;"Starting ""entry""."

2.csv

formatcount;formatname;printername;Beschreibung
2         ;Yellow;Flower;

3.csv

formatcount;formatname;printername;Beschreibung
3         ;Grey;Rock;"Let's have a ; here."

希望我留下了足够的可调节部件供您根据需要进行更改。

注意如果查询被修改为使用参数,例如您可能希望将10007% 变成一个变量,然后您必须使用 SQL 参数来防止 SQL 注入。

关于向数据表添加列。

添加列是个好主意,但不要像这样添加带有额外数据的行:

daten.Rows.Add(daten.Rows(0).Item("Description"), daten.Rows(0).Item("Description 2"), Anzahl, Format, Drucker)

您需要将额外的数据添加到每一行,如下所示:

For i = 0 To daten.Rows.Count - 1
    daten.Rows(i)("formatcount") = Anzahl
    daten.Rows(i)("formatname") = Format
    daten.Rows(i)("printername") = Drucker
Next

【讨论】:

  • 我真的需要 CSVLine 函数吗?我之前在没有它的情况下进行了导出,它对我来说效果很好。有什么好处?
  • 不是 csv 中的所有列。从数据库中填充。所以我只需要使用 2 个不同的“columnsToUse”,一个用于 SQL 语句,一个用于 CSV。写?
  • @JoelCoehoorn 似乎最初不知道要选择哪些列,并且可能会一时兴起对其进行更改。此外,考虑到后来出现的关于将结果与操作员输入的数据相结合的新信息,我不会使用 DataTable。
  • @JoelCoehoorn 我没有写一个全唱全舞的方法来查询数据库,而是添加了一个关于使用 SQL 参数的注释。
  • @AndrewMorton 干得好,这个答案不应该因为害怕 SQL 注入而否定!
【解决方案2】:

根据您的代码,您的 ExportCSV 函数应该类似于下面的代码。
你在参数处理上是错误的。您的数组列表包含字典类型而不是整数类型。
此外,如果您的数组列表为空,请在检索数据或查询组合等时注意 Subs。
但是,请查看此代码并尝试在您的场景中改进它。
我希望它可以帮助你。

Function ExportCSV(ByVal Daten As ArrayList, ByVal Pfad As String) As Boolean

    Dim CSVseparator As String = "|"
    Dim intProgressive As Integer = 0

    Try

        For Each mDict As Dictionary(Of String, Object) In Daten
            Using sr As StreamWriter = New StreamWriter(Pfad & "_" & intProgressive)
                Dim row As String = Join(mDict.Values.ToArray, CSVseparator)
                sr.Write(row)
                intProgressive += 1
            End Using
        Next

        Return True

    Catch ex As Exception
        Console.WriteLine(ex.ToString)
    End Try

    Return False
End Function

【讨论】:

  • ArrayList 不推荐用于新代码。见docs.microsoft.com/en-us/dotnet/api/…
  • 如您所见,我的解释以“基于您的代码……”开头,因此,根据我的回答,您的评论不在正确的位置。我认为在 OP 问题下应该会更好。别跟着我了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-10-04
  • 1970-01-01
  • 2019-06-11
  • 1970-01-01
  • 2023-01-24
  • 1970-01-01
  • 2020-01-02
相关资源
最近更新 更多