none
CSV File nach SQL DB exportieren - Blank wird als 0 angegeben und nicht NULL

    Frage

  • Hallo zäme

    Ich komme nicht mehr weiter: Wenn ich das CSV-File mit Blank Werten in die SQL DB exportiere, werden diese in der DB als 0 angegeben. Was in dem Fall nicht korrekt ist, es sollten NULL Werte angezeigt werden.

    Datentyp in der SQL DB sind Integer NULL.

    die csv Daten sehen wie folgt aus (Result-Barbara.csv):

    HostName,OSType,OSVersion,OSEdition,OSArchitecture,PhysicalRAM,LogicalRAM,NumProcessor,NumLogProcessor,CoresPerProcessor
    w99r0002,Windows,Server 2012 R2,Standard Edition,x86_64,131072,131045,2,,10
    lb1r0023.imobi.mobicorp.ch,Red Hat Enterprise Linux,6.4,Server,x86_64,3840,3825,,4,
    lb1r0026.imobi.mobicorp.ch,Red Hat Enterprise Linux,6.4,Server,x86_64,1888,1869,,2,
    ld1c0469.umobi.mobicorp.test,Red Hat Enterprise Linux,6.8,Server,x86_64,,32101,2,8,
    ld1c046d.umobi.mobicorp.test,Red Hat Enterprise Linux,7.3,Server,x86_64,,3783,2,2,1

    Das Script sieht wie folgt aus:

    $swdlist  = Import-CSV "D:\ADDM\DBA1\Result-Barbara.csv"
    $SQLServer = "s91524"
    $table = 'dbo.ZDM_ADDM_SERVER_INFO'

    Write-Output "Connection $SQLServer db=$SQLDBName opened." $dbwrite = $SqlConnection.CreateCommand() $dbwrite.CommandText = "TRUNCATE TABLE $table" $dbwrite.ExecuteNonQuery() | out-null Write-Output "Table $table truncated." Write-Output " " $N = $swdlist.Length $i = 'NULL' Foreach($swd in $swdlist){ $i++ Write-Progress -Activity "Inserting rows" -Status "done: $i/$N" -PercentComplete (($i / $N) * 100) $col1 = $swd.HostName $col2 = $swd.OSType $col3 = $swd.OSVersion $col4 = $swd.OSEdition $col5 = $swd.OSArchitecture $col6 = $swd.PhysicalRAM $col7 = $swd.LogicalRAM $col8 = $swd.NumProcessor $col9 = $swd.NumLogProcessor $col10 = $swd.CoresPerProcessor $dbwrite = $SqlConnection.CreateCommand() $dbwrite.CommandText = "INSERT into $table (HostName, OSType, OSVersion, OSEdition, OSArchitecture, PhysicalRAM, LogicalRAM, NumProcessor, NumLogProcessor, CoresPerProcessor) VALUES ('$col1','$col2','$col3','$col4','$col5','$col6','$col7','$col8', '$col9', '$col10')" $dbwrite.ExecuteNonQuery() | out-null }

    Kann mir da jemand weiterhelfen?

    Danke

    Mittwoch, 8. November 2017 12:58

Antworten

  • Der Performancevorteil liegt nicht in der Verwendung von NULL oder DBNull sondern in der Verwendung von Parametern in SQL. Der SQL-Server (und andere DB's) müssen dann nicht jedesmal die Syntax neu analysieren, ggf. Zugriffspfade prüfen (bei Where-Klauseln), usw.
    Auch der Insert wird dann durchaus schneller.

    Das andere Problem ist die unterschiedliche Bedeutung von DBNull und $null.
    Dies haben auch andere Sprachen, denn DBNull ist nie NULL, da es einen speziellen Datenbankwert darstellt und keinen leeren Verweis ($null).

    Ein sehr schönes Beispiel findest du hier:
    http://nycdotnet.blogspot.de/2013/05/performing-insert-from-powershell-script.html

    Bei der Zuweisung eines Parameterwertes entscheidest du eben, ob DBNULL oder der Wert übertragen wird:

    $sqlCommand.Parameters[0].Value = [DBNull]::Value

    Wenn man es denn übersichtlich haben will, baut man sich einen IIF:
    https://stackoverflow.com/questions/25682507/powershell-inline-if-iif

    Donnerstag, 9. November 2017 15:07

Alle Antworten

  • In deinem SQL-Insert sorgst du nicht dafür, dass auch NULL's übergeben werden können.
    Dazu musst du dem Command-Objekt je Feld einen Parameter hinzufügen und beim Füllen der Parameter abfragen, ob der Quellwert durch NULL ersetzt werden soll.
    Ich finde im Moment allerdings zuviele Seiten, wie man mit Parametern für SQL in der Powershell umgeht.

    Du kannst allerdings auch den Import-Wizzard des SQ-Servers verwenden:
    https://dba.stackexchange.com/questions/123184/how-to-import-blanks-as-nulls-instead-of-zeros-while-importing-txt-using-wizard


    • Bearbeitet bfuerchau Mittwoch, 8. November 2017 13:39
    Mittwoch, 8. November 2017 13:38
  • Moin,

    für mich haben sich für diese Anforderungen zwei Verfahren herauskristallisiert, die ich je nach Umfang anwende:

    1. Wenn es nur um einige wenige Felder geht, die NULL beinhalten können, nutze ich

    if ($col42 -eq $null) {
        $dbcol42 = 'NULL'
    } else {
       $dbcol42 = "'$dbcol42'"
    }
    ...
    $sql = "INSERT INTO XTABLE (MyCol42) VALUES ($dbcol42)"

    NB: Im SQL-Text selber sind keine Anführungszeichen mehr!

    2. Sind viele Felder auf diese Art zu prüfen, schließe ich sie einfach aus der SQL-Abfrage aus:

    # irgendein Wert, der nie NULL sein wird
    $cols = "Hostname"
    $vals = "'$col1'"
    
    # und für jeden "verdächtigen" Wert...
    if ($col42 -ne $null) {
        $cols += ',MyCol42'
        $vals += ",'$col42'"
    }
    
    $sql = "INSERT INTO XTABLE ($cols) VALUES ($vals)"
    oder so ähnlich. Dann tauchen die NULL-Felder in der Abfrage gar nicht auf und werden, wenn man keinen DEFAULT-Wert vorgegeben hat, mit NULL initialisiert.

    Alles natürlich verbesserungsfähig, führt aber zum Ziel.


    Evgenij Smirnov

    I work @ msg services ag, Berlin -> http://www.msg-services.de
    I blog (in German) @ http://it-pro-berlin.de
    my stuff in PSGallery --> https://www.powershellgallery.com/profiles/it-pro-berlin.de/
    Exchange User Group, Berlin -> http://exusg.de
    Windows Server User Group, Berlin -> http://www.winsvr-berlin.de
    Mark Minasi Technical Forum, reloaded -> http://newforum.minasi.com

    Mittwoch, 8. November 2017 16:32
  • Kein Lösungsansatz funktionierte, wo das Blank im CSV als NULL in die DB schreiben kann.

    Die Umgehungslösung ist, die Objekte die ein Blank haben könnten, jeweils nicht in die DB importieren. Das sieht dann im Script wie folgt aus:

    Foreach($swd in $swdlist){ # CSV format: HostName, OSType, OSVersion, OSEdition, OSArchitecture, PhysicalRAM, LogicalRAM, NumProcessor, NumLogProcessor, CoresPerProcessor $i++ # Progress Bar Write-Progress -Activity "Inserting rows" -Status "done: $i/$N" -PercentComplete (($i / $N) * 100) $Feldname = "HostName, OSType, OSVersion, OSEdition, OSArchitecture" $col1 = $swd.HostName $col2 = $swd.OSType $col3 = $swd.OSVersion $col4 = $swd.OSEdition $col5 = $swd.OSArchitecture $Values = "'$col1','$col2','$col3','$col4','$col5'" $col6 = $swd.PhysicalRAM.Trim() if ($col6 -ne "") { $Feldname = "$($Feldname),PhysicalRAM" $Values = "$($Values),'$($col6)'" } $col7 = $swd.LogicalRAM.Trim() if ($col7 -ne "") { $Feldname = "$($Feldname),LogicalRAM" $Values = "$($Values),'$($col7)'" } $col8 = $swd.NumProcessor.Trim() if ($col8 -ne "") { $Feldname = "$($Feldname),NumProcessor" $Values = "$($Values),'$($col8)'" } $col9 = $swd.NumLogProcessor.Trim() if ($col9 -ne "") { $Feldname = "$($Feldname),NumLogProcessor" $Values = "$($Values),'$($col9)'" } $col10 = $swd.CoresPerProcessor.Trim() if ($col10 -ne "") { $Feldname = "$($Feldname),CoresPerProcessor" $Values = "$($Values),'$($col10)'" } write-host $Feldname write-host $Values $query = "insert into $table ($Feldname) VALUES ($values)" write-host $query $dbwrite = $SqlConnection.CreateCommand() $dbwrite.CommandText = "INSERT into $table ($Feldname) VALUES ($values)" $dbwrite.ExecuteNonQuery() | out-null }


    Und das funktioniert:-)

    Donnerstag, 9. November 2017 11:47
  • Die Umgehungslösung ist, die Objekte die ein Blank haben könnten, jeweils nicht in die DB importieren. 

    Nun, das war ja genau mein zweiter Ansatz, bis auf die Erkennung leerer Werte in Ausgangsdaten ($null vs. '')


    Evgenij Smirnov

    I work @ msg services ag, Berlin -> http://www.msg-services.de
    I blog (in German) @ http://it-pro-berlin.de
    my stuff in PSGallery --> https://www.powershellgallery.com/profiles/it-pro-berlin.de/
    Exchange User Group, Berlin -> http://exusg.de
    Windows Server User Group, Berlin -> http://www.winsvr-berlin.de
    Mark Minasi Technical Forum, reloaded -> http://newforum.minasi.com

    Donnerstag, 9. November 2017 11:55
  • Wenn du (vernünftiger weise) mit Parametern im SQL umgehen würdest, könntest du geziel jedem Feld als Parameterwert eben den DBNull-Wert übergeben. Das ist einfacher als gedacht und auch noch performanter.
    Donnerstag, 9. November 2017 14:22
  • Wenn du (vernünftiger weise) mit Parametern im SQL umgehen würdest, könntest du geziel jedem Feld als Parameterwert eben den DBNull-Wert übergeben. Das ist einfacher als gedacht und auch noch performanter.

    Moin,

    ja, ein guter Ansatz, den ich auch mal versucht habe zu verfolgen.

    Das Problem dabei (in meinen Augen und in meiner Erfahrung) ist dies:

    Nach meinem beschränkten Verständnis bedeutet dies, dass man DENNOCH jede Variable explizit prüfen muss, selbst wenn wir $null abfangen wollen. Und da im Beispiel des TO der Fall noch schlimmer steht, weil die problematischen Werte leere Strings sind, sehe ich durch Parametrisierung in diesem konkreten Fall keinen Vorteil im Handling.

    Wenn Du ein Code Snippet zur Verfügung stellen magst, würde ich sehr gerne prüfen, ob das wenigstens einen Performance-Vorteil mit sich bringt.


    Evgenij Smirnov

    I work @ msg services ag, Berlin -> http://www.msg-services.de
    I blog (in German) @ http://it-pro-berlin.de
    my stuff in PSGallery --> https://www.powershellgallery.com/profiles/it-pro-berlin.de/
    Exchange User Group, Berlin -> http://exusg.de
    Windows Server User Group, Berlin -> http://www.winsvr-berlin.de
    Mark Minasi Technical Forum, reloaded -> http://newforum.minasi.com

    Donnerstag, 9. November 2017 14:33
  • Der Performancevorteil liegt nicht in der Verwendung von NULL oder DBNull sondern in der Verwendung von Parametern in SQL. Der SQL-Server (und andere DB's) müssen dann nicht jedesmal die Syntax neu analysieren, ggf. Zugriffspfade prüfen (bei Where-Klauseln), usw.
    Auch der Insert wird dann durchaus schneller.

    Das andere Problem ist die unterschiedliche Bedeutung von DBNull und $null.
    Dies haben auch andere Sprachen, denn DBNull ist nie NULL, da es einen speziellen Datenbankwert darstellt und keinen leeren Verweis ($null).

    Ein sehr schönes Beispiel findest du hier:
    http://nycdotnet.blogspot.de/2013/05/performing-insert-from-powershell-script.html

    Bei der Zuweisung eines Parameterwertes entscheidest du eben, ob DBNULL oder der Wert übertragen wird:

    $sqlCommand.Parameters[0].Value = [DBNull]::Value

    Wenn man es denn übersichtlich haben will, baut man sich einen IIF:
    https://stackoverflow.com/questions/25682507/powershell-inline-if-iif

    Donnerstag, 9. November 2017 15:07