Ralf Hüsing

Mai 14, 2009

Deadlockopfer

Abgelegt unter: Programmierung, SQL — ralf @ 11:52 am

“Die Transaktion (Prozess-ID xxxx) befand sich auf Sperre Ressourcen aufgrund eines anderen Prozesses in einer Deadlocksituation und wurde als
Deadlockopfer ausgewählt. Führen Sie die Transaktion erneut aus.”

Soweit ich das verstanden habe treten Deadlock’s auf wenn zwei Transaktionen dauerhaft auf eine bestimmte Ressource warten würden. Eine Ressource kann zbspl. eine Speicher-Seite sein. Der MS-SQL-Server erkennt das und wählt dann einen Prozess aus der gekillt wird.

Das ganze ist im MSDN recht umfangreich beschrieben: Deadlocks.

März 31, 2009

Zahlen sortieren mal anders

Abgelegt unter: Hirndefekt, SQL — ralf @ 12:07 am

So könnte man Zahlen mal auf eine andere Art sortieren:

SELECT 3 AS ID
UNION ALL
SELECT 1 AS ID
UNION ALL
SELECT 2 AS ID
ORDER BY ID

Sieht dann in etwa so aus:

SQL-Sort

Dezember 12, 2008

Eingabedaten flexibel und unabhängig von der Programmiersprache mittels SQL prüfen

Abgelegt unter: Programmierung, SQL — ralf @ 7:05 pm

Möchte man Eingaben flexibel und evtl. komplexen Prüfungen unterziehen kann man sich die Fähigkeiten von SQL zu nutze machen.
Ich gehe hier davon aus das die Anwendung generell eine Datenbankverbindung hat und nicht extra wegen der Prüfung eine Verbindung aufbauen muss.

Ein Beispiel:
Der Benutzer gibt eine Zeichenfolge ein: “D016381″.

Die Anwendung muss nun etwas bestimmtes tun wenn:
Die Eingabe mit D beginnt, gefolgt von einer 0 und .. (was auch immer).

In jedem Falle ist die Prüfung nicht trivial. Hier bieten sich reguläre Ausdrücke an, jedoch ist nicht in allen Programmiersprachen “mal eben so” möglich RegEx zu verwenden.

Diese Prüfung kann sehr einfach mittels SQL umgesetzt werden.
Es bietet sich der LIKE oder REGEXP Vergleichsoperator an:

SELECT -1 AS OK WHERE 'D016381' LIKE 'D0%'

Diese Abfrage liefert einen Datensatz (mit einer Spalte “OK” mit dem Wert -1) wenn der Benutzer D gefolgt von einer 0 gefolgt von “irgendwas” eingegeben hat.

Es ist nun natürlich ohne weiteres möglich die komplexität der Abfrage zu erhöhen:

SELECT -1 AS OK WHERE ('D016381' LIKE 'D0%' AND 'D016381' NOT LIKE '%2') OR ('D016381' LIKE 'K%')

Heißt, wenn die Eingabe:

(D gefolgt von einer 0 gefolgt von “irgendwas” UND die Eingabe nicht auf 2 endet) ODER die Eingabe mit einem “K” beginnt dann… soll die Anwendung etwas tun.

In der Endkonsequenz kann man diese Filter (bzw. SQL-Statements) in einer SQL-Tabelle speichern und zur Laufzeit das jeweilige SQL-Statement generieren und ausführen.
Somit ist man extrem flexibel da man die Abfragen / Prüfungen jederzeit (für alle Anwendungen die diese SQL-Tabelle verwenden) anpassen kann.

September 26, 2008

Den Inhalt aller Tabellen einer Microsoft SQL-Datenbank löschen

Abgelegt unter: Programmierung, SQL — ralf @ 11:42 pm

Mit folgendem TSQL-Script wird der Inhalt aller in der aktuellen Datenbank vorhandenen Benutzer-Tabellen gelöscht:

Sollte TRUNCATE TABLE wg. Referenzen oä. nicht funktionieren kann es durch das etwas langsamere DELETE FROM ersetzt werden.

Bevor man das Script ausführt sollte man mehrfach Prüfen das man in der richtigen Datenbank / auf dem richtigen SQL Server ist.

DECLARE @table varchar(128)

DECLARE CUR CURSOR FOR
SELECT sysobjects.name FROM sysobjects WHERE type = 'U'

OPEN CUR

FETCH NEXT FROM CUR INTO @table

WHILE @@FETCH_STATUS = 0
BEGIN
	EXECUTE ('TRUNCATE TABLE [' + @table + ']')
	FETCH NEXT FROM CUR INTO @table
END

CLOSE CUR
DEALLOCATE CUR

Mai 3, 2008

Sichten und Vernichten

Abgelegt unter: Hirndefekt, Programmierung, SQL — ralf @ 6:14 pm

Eine sehr übersichtliche SQL Sicht:

SQL Sicht

Januar 13, 2008

Microsoft SQL Datenbank Offline Schalten

Abgelegt unter: Programmierung, SQL — ralf @ 7:53 pm

Folgendes SQL Script killt ALLE Prozesse die mit einer bestimmten SQL-Datenbank verbunden sind und schaltet die Datenbank offline.

Da ich gerade nicht weiß wie man einen Datenbank namen als Objekt (DECLARE .. database) definiert muss man den Datenbanknamen zweimal angeben, einmal oben bei “SET @db..” und einmal unten bei “ALTER DATABASE”.


USE [master]

DECLARE @spid int
DECLARE @db varchar(128)

SET @db = ‘–DatenbankName–’

DECLARE CUR CURSOR FOR
SELECT spid FROM sysprocesses WHERE dbid = DB_ID(@db) AND hostname <> ” AND spid <> @@SPID

OPEN CUR

FETCH NEXT FROM CUR INTO @spid

WHILE @@FETCH_STATUS = 0
BEGIN
EXECUTE (’KILL ‘ + @spid)
FETCH NEXT FROM CUR INTO @spid
END

CLOSE CUR
DEALLOCATE CUR

SELECT @db AS ‘DB’, DATABASEPROPERTYEX (@db, ‘Status’) AS ‘State before’

ALTER DATABASE –DatenbankName– SET OFFLINE

SELECT @db AS ‘DB’, DATABASEPROPERTYEX (@db, ‘Status’) AS ‘State after’

Januar 12, 2008

Prozesse von Microsoft SQL Server Killen

Abgelegt unter: Programmierung, SQL — ralf @ 9:13 pm

Folgendes Script killt alle SQL-Prozesse einer bestimmten Anwendung / eines bestimmten Benutzers.
MS-SQL macht teilweise richtig Spass :-)


DECLARE @spid int

DECLARE CUR CURSOR FOR
SELECT spid FROM sysprocesses WHERE program_name = 'Name der Anwendung' AND loginame = 'Name des SQL Benutzers'

OPEN CUR

FETCH NEXT FROM CUR INTO @spid

WHILE @@FETCH_STATUS = 0
BEGIN
EXECUTE ('KILL ‘ + @spid)
FETCH NEXT FROM CUR INTO @spid
END

CLOSE CUR
DEALLOCATE CUR

Dezember 31, 2007

Microsoft SQL Server 2000 Partitionen

Abgelegt unter: Programmierung, SQL — ralf @ 3:47 am

Da Wochenende war kam bei mir wiedermal der Spieltrieb durch.
Ich wollte mir schon immer mal die Partitionierung von Tabellen im SQL Server 2000 näher anschauen.

Vorweg sei zu erwähnen das die Paritionen erst mit SQL 2005 halbwegs vernünftig implementiert sind.
Partitionen in SQL 2000 (oder gar SQL 7) haben folgende (gravierende Nachteile):

  • Es müssen viele einzelene Tabellen angelegt bzw. verwaltet werden.
  • Bei INSERT-Anweisungen müssen immer alle Felder angegeben werden, auch wenn diese Default-Werte haben.
  • Die Tabellen dürfen keine Spalte vom Typ “timestamp” haben.
  • Die Tabellen dürfen keine Autoinkrement (Identitäts)-Spalte haben.
  • Weitere Nachteile (zBspl. bei UPDATE) sind dem MSDN zuentnehmen.

Ok, das waren die Nachteile, evtl. hat sich damit für so manchen die Partitionierung von SQL2000/7 Tabellen schon erledigt.
Nichts destotrotz gehts weiter mit etwas Theorie:

Die Partitionierung funktioniert über mehrere Tabellen, die nur definierte Daten beinhalten dürfen. Diese “Definition” wird über eine CHECK Einschränkung sichergestellt. Hat man die Tabellen angelegt wird eine Sicht erstellt die sich über alle Tabellen erstreckt und in etwa wie folgt aussieht:

SELECT * FROM dbo.part0
UNION ALL
SELECT * FROM dbo.part1
UNION ALL
SELECT * FROM dbo.part2
....

Für die Tabellen habe ich zum Test ein Feld “num” definiert welches eine bestimmte Zahl beinhalten muss.
Es gibt, in meinem Test, vier Tabellen (part0 .. part3). Jede Tabelle hat folgende Felder: num, id, text. Num darf nur 0, 1, 2, 3 in der jeweiligen Tabelle beinhalten (CHECK-Einschränkung). Das Feld id ist irgendeine eindeutige Zahl. Das Feld text hat keine Bedeutung. num und id sind Primärschlüssel.
Hier die CREATE Anweisung für Tabelle “part0″:

CREATE TABLE [dbo].[part0] (
[num] [int] NOT NULL ,
[id] [int] NOT NULL ,
[text] [varchar] (50) COLLATE Latin1_General_CI_AS NULL
) ON [PART0]
GO

ALTER TABLE [dbo].[part0] WITH NOCHECK ADD
CONSTRAINT [PK_part0] PRIMARY KEY CLUSTERED
(
[num],
[id]
) ON [PART0]
GO

ALTER TABLE [dbo].[part0] ADD
CONSTRAINT [CK_part0] CHECK ([num] = 0)
GO

Der Primärschlüssel muss so gewählt sein das er in jeder Tabelle immer Eindeutig ist.

Nachdem die Tabellen angelegt wurden ist noch die bereits oben beschriebene Sicht zu erstellen:

CREATE VIEW dbo.part
AS
SELECT *
FROM dbo.part0
UNION ALL
SELECT *
FROM dbo.part1
UNION ALL
SELECT *
FROM dbo.part2
UNION ALL
SELECT *
FROM dbo.part3

Die jeweilige Anwendung verwendet die Sicht “part” um Daten abzurufen, anzulegen, zu löschen bzw. zu Modifizieren:

SELECT * FROM part WHERE num = 1
INSERT INTO part (num, id, text) VALUES (1, 2, 'text..')
UPDATE part SET text = 'mehr ' + text WHERE num = 1 AND id = 2
DELETE FROM part WHERE num = 1 AND id = 2

Soweit so gut. Nun stellt sich natürlich die Frage: “Was bringts?”.
Um das zu beantworten gilt es den “SQL Query Analyzer” zu befragen:


SELECT

Es sieht hier so aus als ob der SQL Server alle Tabellen anfragen würde. Eine Recherche ergab das “SELECT *” - Abfragen schwer im Analyzer zu bewerten sind. Abhilfe schafft hier “SELECT TOP 100 PERCENT *”:


SELECT-TOP

Man sieht genau das was zu erwarten war. Es wird nur eine Tabelle (part1 - “num = 1″) abgefragt statt alle.

Aber die Frage “was es bringt” ist immer noch nicht beantwortet.

Aufgrund der Tatsache das die Tabellen nach dem Erstellen keine bzw. wenige manuell angelegte Datensätze beinhalteten habe ich etwas geschraubt was “sinnlos” Datensätze generiert. Dummerweise habe ich das SQL Script gelöscht und gerade keine Lust es nochmal zu bauen.
Die schiere Menge der Daten, die nach 2 Tagen Laufzeit, generiert wurden bitte ich dem folgenden Screenshot zu entnehmen:


Dateien

Wieviele Datensätze genau vorhanden sind kann ich nicht wirklich beantworten weil ich keine Lust habe Stunden zu warten bis ein “SELECT num, count (*) as Anzahl FROM part GROUP BY num” fertig ist.
Was ich mit Sicherheit sagen kann ist das die Tabelle “part1″ genau zwei Datensätze beinhaltet und SQL-Abfragen gegen diese Tabelle “..WHERE num = 1..” gewohnt schnell ablaufen.

Fazit:
Partitionen - in SQL 2000 - sind toll, haben einen hohen Spiel-Faktor aber reinpraktisch gesehen keinen Sinn.
Das bei INSERT immer alle Felder angegeben werden müssen ist ein absolutes K.O.-Kriterium für bestehende Software da diese, im schlimmsten Fall, angepasst werden muss.
Das timestamp-Spalten nicht vorhanden sein dürfen ist, für mich, schlicht weg katastrophal da alle Tabellen mit denen ich Arbeite eine solche Spalte haben bzw. haben müssen.

September 11, 2007

Nächste Freie Lücke in Datensätzen

Abgelegt unter: Programmierung, SQL — ralf @ 7:03 pm

Folgendes SQL Statement ermittelt die nächsten freien Datensätze innerhalb einer teilweise gefüllten Tabelle:

SELECT
t1.id + 1 AS id
FROM
tabelle t1
WHERE
t1.id + 1 NOT IN (SELECT t2.id FROM tabelle t2 WHERE t2.id = t1.id + 1)
ORDER BY t1.id ASC

Ausgebend davon das die tabelle bereits die id’s 1, 2, 4, 5 und 80 beinhaltet, gibt obiges SQL Statement die id’s 3, 6 und 81 zurück.

Getestet habe ich das ganze mit MS-SQL 2000 sowie MySQL-5.

Ist die Tabelle leer liefert das SQL Statement keinen Datensatz zurück.

Auf dem betreffenden Feld (in diesem Beispiel “id”) sollte aus Performance Gründen natürlich ein Index liegen.

Ist man nur an dem ersten freien Datensatz interessiert kann ein “TOP 1″ oder “LIMIT 1″ an passender Stelle angefügt werden.

Ist man an dem größten Freiem Wert interessiert (hier wäre dies 81) sortiert man einfach absteigend (ORDER BY t1.id DESC).

Powered by WordPress ( WordPress Deutschland )