Archiv der Kategorie: Datenbanken

Unter Datenbanken verstehe ich alle persistenten Speicherungssysteme die über ein relationales, objektorientiertes oder hierarchisches Datenmodell und über eine standardisierte Abfragesprache wie SQL, XQuery etc… verfügen.
Auch Datenbank-APIs wie Hibernate passen in diese Kategorie.

VBA und Access : Datenbankzugriff von VBA auf .mdb-Datei

Aufgabenstellung

Von einer Office/VBA-Anwendung aus, soll auf eine Microsoft Access Datenbank (.mdb-Datei) zugegriffen werden.

Vorraussetzungen

Im VBA Editor im Menüpunkt Verweise wird die Option Microsoft ActiveX ADO Objects angehakt.

Lösung

Selektionsanweisung

Public Sub HoleDaten()
   Dim cn As New ADODB.Connection
   Dim rs As New ADODB.Recordset
   Dim i As Integer

' Datei liegt im aktuellen Projektverzeichnis
   cn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & CurrentProject.Path & _
                 "\diedatei.mdb"

   Set rs = cn.Execute("SELECT * FROM einetabelle")

   i = 1
   Do While Not rs.EOF
      Cells(i, 1).Value = rs.Fields("Feld1")
      Cells(i, 2).Value = rs.Fields("Feld2")
      Cells(i, 3).Value = rs.Fields("Feld3")
      Cells(i, 4).Value = rs.Fields("Feld4")
      rs.MoveNext
      i = i + 1
   Loop
   cn.Close
End Sub

Manipulationsanweisung

Public Sub SchubseInMDB()
   Dim cn As New ADODB.Connection
   Dim rs As New ADODB.Recordset

   cn.Open "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=unfug.mdb"

   Set rs = cn.Execute("INSERT INTO einetabelle(zweck)VALUES ('Unsinn')")
   cn.Close
End Sub

MS SQL Server: Split/Explode für SQL

Aufgabenstellung

Eine Spalte einer Tabelle beinhaltet eine Auflistung, die durch ein Zeichen getrennt ist. Im Beispiel die Spalte Gruppe.

Nr|Gruppe
-------------------------------
1 | Buerste,Besen,Feger
2 | Eier,Salz,Butter
3 | Bier,Schnaps,Wein 

Es soll nun mit einer Art Split/Explode wie in PHP die Spalte aufgesplittet werden und einzelne Werte ausgegeben werden

Ansatz

Der folgende Code erstellt eine Funktion, die für eine Datenbank gilt. Kopiert man diesen Code in das SQL Server Management Studio und führt ihn mit F5 aus, erhält die ausgewählte Datenbank die Funktion Split.

Lösung

CREATE FUNCTION [dbo].[split](@String varchar(8000), @Delimiter varchar(12), 
                                                 @returnItem int)
RETURNS varchar(8000)
AS
BEGIN
	DECLARE @id int
	DECLARE @idx int
	DECLARE @slice varchar(8000)
	DECLARE @returnslice varchar(8000)

	SET @id = 0
	SET @idx = 1
	SET @returnslice = null
	
	WHILE @idx!= 0
	BEGIN
		SET @id = @id + '1'
		SET @idx = charindex(@Delimiter,@String)

		IF @idx!=0
			SET @slice = left(@String,@idx - 1)
		ELSE
			SET @slice = @String

		IF @id = @returnItem SET @returnslice = @slice
		SET @String = SUBSTRING(right(@String,len(@String) - @idx),
                                                  len(@Delimiter), len(@String))

		IF len(@String) = 0 BREAK
	END
	RETURN @returnslice
END

Aufruf mit:

SELECT dbo.split('DIES/IST/EIN/TEST','/',2)

MS SQL Server: Ergebnisvariable aus EXEC SQL-Statement

Aufgabenstellung

Aus einem normalen SQL Statement, welches mit EXEC ausgeführt wird, soll eine Ergebnisvariable in dem „normalen“ TSQL-Skript weiterverwendet werden.

Ansatz

Hierzu wird der Rückgabewert des EXEC Statements in eine Variable geschrieben.

Lösung

SET @sql2='SELECT COUNT(*) FROM users WHERE ['+@forsheet+']=1'
		
IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES 
                WHERE TABLE_NAME = 'ResultSet') 
DROP TABLE ResultSet;
		
CREATE TABLE ResultSet (mycount int)
 
INSERT INTO ResultSet
EXEC sp_executesql @sql2
		
SELECT @isdrin=mycount FROM ResultSet

MS SQL Server: DROP TABLE wenn die Tabelle nicht existiert

Aufgabenstellung

Eine Tabelle soll gelöscht werden wenn sie existiert.

Problem

Nutzt man in einem TSQL-Skript den DROP Table Befehl (z.B. in einer Stored Procedure) erhält man eine Fehlermeldung, das die zu löschende Tabelle nicht existiert. In einigen Fällen führt dies zum Abbruch des Skriptes.

Ansatz

Über das META-Schema vom MS SQL Server lässt sich erfragen, ob eine Tabelle existiert.

Lösung

IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'users') 
DROP TABLE users;

MS SQL Server und ACCESS: Führende Nullen in numerischem String abschneiden

Aufgabenstellung

In einer Spalte mit Materialnummer/Artikelnummern werden bei allen numerischen Materialnummern die Nullen entfernt, alphanumerische Materialnummern bleiben unberührt. Soll auch bei den alphanumerischen Materialnummern die führenden Nullen entfernt werden, verweise ich auf den Artikel in der Rubrik „MS SQL Server: Führende Nullen bei alphanumerischem String“.

Ansatz

Über das Case Statement wird gefragt ob der Artikel numerisch ist. Ist er numerisch, wird über die Konvertierung in einen Integer (und anschließendes Zurückkonvertieren in VARCHAR) die führenden Nullen entfernen.

Lösung

Microsoft SQL Server:

UPDATE tabelle SET material=
CASE WHEN isnumeric(material)=1 
        THEN convert(VARCHAR, convert(INTEGER, material))
        ELSE convert(VARCHAR, material)
END 

Microsoft Access:

   UPDATE articletexts SET material=
   IIF(IsNumeric(material), 
   REPLACE(LTRIM(REPLACE(material,'0',' '))
   ,' ','0'), material) 

C# und OleDBParameter: Reihenfolge von Parametern

Problem

(Ein kleiner Schmunzelartikel)… Da sitzt man seit Stunden vor einem normal aufgebaut SQL Statement mit einem OleDbCommand… es ist alles so wie bei einen SQLCommand…. und das Statement tut nichts… gar nichts…

Lösung

Die Reihenfolge von OleDBParametern ist NICHT assoziativ wie durch die Namensgebung vermutet… es spielt gar keine Rolle ob ein Statement mit einem ? oder einem @parametername versehen wird. Die Reihenfolge muss immer stimmen, wenn man mit

comm.Parameters.AddWithValue("parametername", variable) 

Microsoft SQL Server: Führende Nullen bei alphanumerischem String abschneiden

Problem

Ein String (z.B. eine Materialnummer oder eine Artikelnummer) besteht aus alphanumerischen Zeichen (z.B. 00003j422a3) und hat führende Nullen, welche abgeschnitten werden sollen (Ergebnis: 3j422a3). Siehe auch „Führende Nullen bei numerischen Strings abschneiden“.

Ansatz

* Zuerst werden ALLE Nullen durch Leerzeichen ersetzt
* danach werden alle Leerzeichen auf der linken Seite mit LTRIM abgeschnitten
* anschließend werden alle Leerzeichen durch 0en ersetzt.

Prämissen / Vorraussetzungen

Es sind keine Leerzeichen in dem String

Lösung

SELECT REPLACE(LTRIM(REPLACE(artikelnummer, '0', ' ')), ' ', '0') FROM dbo.cube1

MS SQL Server-Tabelle mit Pivot drehen (z.B. für Zeitreihenanalyse)

Problemfeldbeschreibung

Beim Arbeiten mit Daten im Microsoft SQL Server kommt man gelegentlich an den Punkt, an dem man sich das „Drehen“ von Tabellendaten als nützlich entpuppt. Anwendungsgebiete sind u.a. die Prognoserechnung, Zeitreihenanalyse oder einfach Matrizenmultiplikation.

Nehmen wir z.B. den folgenden Datensatz, er soll gedreht werden:

200901     1    2009    0    30
200902     2    2009    58    16
200903     3    2009    520    15
200906     6    2009    780    15
200809     9    2008    0    7
200909     9    2009    260    8
200810    10    2008    0    

Der unerfahrene Datenbankentwickler beginnt dann häufig mit der Implementierung von langsamen Cursorskripten á la

DECLARE @compcode varchar(255)
DECLARE @market varchar(255)
DECLARE @salesdiv varchar(255)
DECLARE @material varchar(255)
DECLARE @month int
DECLARE @year int
DECLARE @bruttoumsatz varchar(255)
DECLARE @amount varchar(255)
DECLARE @spalte varchar(255)

DECLARE mmg_cursor CURSOR FOR
SELECT compcode, market, salesdiv, material, month, year, bruttoumsatz, amount
FROM cube WHERE CAST(CAST(year as varchar)+(CASE WHEN month<10 THEN ('0'+CAST(month as varchar)) ELSE CAST(@month as varchar) END ) as int) >=200610

OPEN mmg_cursor

FETCH NEXT FROM mmg_cursor INTO @compcode,@market, @salesdiv, @material,
@month, @year, @bruttoumsatz, @amount

WHILE @@FETCH_STATUS = 0
BEGIN

SET @spalte=CAST(@year as varchar)+(CASE WHEN @month<10 THEN ('0'+CAST(@month as varchar)) ELSE CAST(@month as varchar) END ) EXEC('UPDATE hypercube SET ['+@spalte+']='+@amount+' WHERE [compcode]='''+@compcode+''' AND [salesdiv]='''+@salesdiv+''' AND [market]='''+@market+''' AND [material]='''+@material+''' ') FETCH NEXT FROM mmg_cursor INTO @compcode,@market, @salesdiv, @material, @month, @year, @bruttoumsatz, @amount END DEALLOCATE mmg_cursor [/javascript]

Ansatz

Es bedarf keines Benchmarks oder einer Messung um festzustellen, dass diese Methode sehr zeitaufwändig und unzeitgemäß ist. Microsoft rät von solchen Cursor-Skripten ab und rät von der Nutzung der PIVOT-Funktion, die an Performance und Handhabung kaum zu überbieten ist.

Lösung

Das folgende SQL-Skript dreht die Tabelle mit PIVOT (Die MS SQL Server-Datenbank muss hierzu mind. auf MSSQL2005-Kompatibilität gestellt werden.

declare @columns varchar(max)
declare @columnsoben varchar(max)
declare @count int
declare @max int
declare @current int
declare @sql nvarchar(max)
declare @t table(id int identity(1,1), datekey int)
insert into @t select distinct datekey from vglobe2 order by 1
set @columns = “
set @columnsoben = “
set @count = 1
set @max = (select max(id) from @t)
while (@count <= @max) begin set @current = (select datekey from @t where id = @count) set @columns = @columns + '[' + cast(@current as varchar(6)) +'], ' set @columnsOben = @columnsOben + '[' + cast(@current as varchar(6)) +'],' + '0 as [' + cast(@current as varchar(6)) +'stat],' set @count = @count+1 end set @columns = substring(@columns, 1, len(@columns)-1) -- letztes komma set @columnsOben = substring(@columnsOben, 1, len(@columnsOben)-1) -- letztes komma set @sql = 'SELECT Artikel, ' + @columnsoben + N' FROM (SELECT artikel, bruttoumsatz , datekey FROM vglobe2) p PIVOT ( sum(bruttoumsatz) FOR datekey in (' + @columns + N') ) AS pvt ORDER BY 1' print @sql exec sp_executesql @sql [/sql]

Massenimport von Daten über MS SQL Server Management Studio

Problem

Eine große CSV Datei soll in eine Tabelle importiert werden. Hierbei sollen nicht alle Felder importiert werden.

Ansatz

Die Verwendung des Kommandos BULK INSERT wurde von Microsoft für sehr große Dateien vorgesehen, um den SQL Server ohne die Verwendung von langsamen Cursor-Iterationen zu füllen

Lösung

Vorgehensweise

Erstellen einer Tabelle mit CREATE TABLE in die importiert wird
Zunächst erstellt man eine Tabelle in die importiert werden soll, hier wird eine fiktive Tabelle RAWBEIL erstellt.

CREATE TABLE [dbo].[rawbeil](
	[gruppe] [varchar](255) NULL,
	[bezeichnung] [varchar](255) NULL,
	[feldname] [varchar](255) NULL,
	[sapcolumn] [varchar](50) NULL,
	[saptable] [varchar](50) NULL
)

Erstellen einer IMPORT-Datei für die Feldzuweisung

Der RECORD-Tag muss den kompletten Aufbau der CSV-Datei enthalten; der ROW-Teil darf enthält die Tabellenspalten, in die importiert wird. FIELD ID wird auf COLUMN SOURCE gemappt. ROW/COLUMN SOURCE kann auch Lücken enthalten, wenn man Felder aus der CSV Datei weglassen möchte..

&lt;?xml version="1.0" ?>
&lt;BCPFORMAT xmlns="http://schemas.microsoft.com/sqlserver/2004/bulkload/format" 
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
	&lt;RECORD>
		&lt;FIELD ID="1" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255"/>
		&lt;FIELD ID="2" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="3" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="4" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="5" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="6" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="7" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="8" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="9" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="10" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="11" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="12" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="13" xsi:type="CharTerm" TERMINATOR=";" MAX_LENGTH="255" />
		&lt;FIELD ID="14" xsi:type="CharTerm" TERMINATOR="\r\n" 
                  MAX_LENGTH="255" />
	&lt;/RECORD>
	&lt;ROW>
		&lt;COLUMN SOURCE="1" NAME="gruppe" xsi:type="SQLVARYCHAR" />
		&lt;COLUMN SOURCE="2" NAME="bezeichnung" xsi:type="SQLVARYCHAR" />
		&lt;COLUMN SOURCE="3" NAME="feldname" xsi:type="SQLVARYCHAR" />
		&lt;COLUMN SOURCE="4" NAME="sapcolumn" xsi:type="SQLVARYCHAR" />
		&lt;COLUMN SOURCE="5" NAME="saptable" xsi:type="SQLVARYCHAR" />
	&lt;/ROW>
&lt;/BCPFORMAT>

SQL Anweisung

CODEPAGE wird verwendet um das Umlaute-Problem zu beheben, FIRSTROW ist die erste Spalte ab der aus CSV importiert wird (meistens will man den Header ausschließen).

DECLARE @sql nvarchar(4000)
SET @sql =
'BULK INSERT rawbeil FROM ''D:\web\karpbjde\import\db.csv''
WITH (
FORMATFILE = ''D:\web\karpbjde\import\beilmade.fmt'',
CODEPAGE=''raw'',
FIRSTROW = 2
)'
EXEC(@sql)

BULK INSERT mit Typecast in einen bestimmten Datentyp

/* Bulk-Import von einer .CSV-Datei in eine Tabelle incl. der Datentypen
* es werden Zahlen im Vormat
* xxx,yy
* xxx.yy
* -xxx,yy
* -xxx.yy
* xxx,yy-
* xxx.yy
* unterstützt. Diese werden als Money-Datentyp convertiert
* Bulk-Import
*
* gewidmet an Björn K.von Nico Gerbig
*/ 

-- Zuerst als Varchar lesen, dann richtig
create table ordersbulk (col1 varchar(50), col2 varchar(50), col3 varchar(50));
create table ordersbulk2 (col1 varchar(50), col2 money, col3 money) 

-- Eintragen in Tabelle mit Varchars
BULK INSERT OrdersBulk
    FROM 'c:\test.csv.txt'
    WITH
    (
        --FIRSTROW = 2,
        FIELDTERMINATOR = ';',
        ROWTERMINATOR = '\n'
    ) 

-- und Kopieren in die Tabelle mit den richtigen Datentypen
insert into ordersbulk2 (col1, col2, col3)
    select col1,
          dbo.ConvertVarcharToMoney(col2),
          dbo.ConvertVarcharToMoney(col3)
    from ordersbulk
go
-- testen
select * from ordersbulk2
go 

-- dafür notwendige funktion (c)  -=e!ninio=-
create function ConvertVarcharToMoney(@abc varchar(255))
    returns money
as begin
    declare @invert money;
    declare @index int;
    declare @newabc money;
    set @invert = 1;
    set @abc = Replace(@abc,',','.')
    set @abc = Replace(@abc,' ','')
    set @index = CHARINDEX('-',@abc)
    if @index &gt; 0
    begin
        set @invert = -1;
        set @abc = Replace(@abc, '-', '')
    end
    set @newabc = CAST(@abc as money) * @invert
    return @newabc
end
go 

-- Test für die Funktion selbst
select dbo.ConvertVarcharToMoney('13,41')
select dbo.ConvertVarcharToMoney('13.41')
select dbo.ConvertVarcharToMoney('-13,41')
select dbo.ConvertVarcharToMoney('13,41-')
select dbo.ConvertVarcharToMoney('- 13,41')
select dbo.ConvertVarcharToMoney('13,41 -')