Wie kann ich in SQL Server 2008 alle Spalten abrufen, die in ihren Schemas als nullbar markiert sind, obwohl keine Datensätze NULL für diese Spalten enthalten?

7

In meiner Legacy-Datenbank lässt das Schema häufig unnötigerweise Null-Einträge für bestimmte Spalten zu. Um herauszufinden, welche Spalten besonders wichtig sind, muss ich SQL Server 2008 abfragen, das über mein Fachwissen hinausgeht. Mein Ziel ist es, das Schema ein wenig zu straffen und mich vor dem Nullfall in meinem Code zu schützen.

Eine grobe Methode (Option 1) zur Lösung meines Problems besteht darin, alle Datensätze abzurufen (und * auszuwählen), die mindestens einen Nulleintrag enthalten, und dann nach Spalten zu suchen, die keine Nulleinträge enthalten. Ich könnte natürlich nur verwenden Where columnA IS NULL OR columnB IS NULL OR columnC IS NULL, aber das wird für Tabellen mit vielen Spalten langweilig. Einige Tabellen in dieser Datenbank enthalten mehr als zwanzig Spalten! Außerdem gibt es insgesamt etwa 500 Tische. Allgemeine Lösungen sind also am besten.

Hier sind drei Optionen für Antworten, die mich zufrieden stellen würden:

  • Option 1: "Holen Sie mir alle Datensätze, bei denen mindestens eine der Spalten null ist". Ich werde nach Spalten ohne Null-Einträge suchen.
  • Option 2: Eine noch bessere Antwort wäre ein Skript, das eine Liste von Spaltennamen erhält, die mindestens einen Nulleintrag enthalten. Dann werde ich manuell sehen, welche nullbaren Spalten nicht in dieser Liste enthalten sind.
  • Option 3: Die beste Antwort würde mir eine Liste von Spaltennamen geben, die in ihrem Schema als nullwertfähig markiert sind, obwohl in diesen Spalten keine Datensätze mit einem Nulleintrag vorhanden sind.

Vielen Dank!

IBC
quelle
Benötigen Sie eine genaue Antwort?
Jack sagt, versuchen Sie topanswers.xyz
2
Nein, ich bin von Beruf Programmierer. Ich kann die kleinen Details herausfinden.
IBC
3
Jetzt sehe ich, dass meine Frage nicht sehr klar war ;-) Müssen Sie genau wissen, wie viele Zeilen nullirgendwo eine haben oder würde eine Schätzung reichen?
Jack sagt, versuchen Sie es mit topanswers.xyz
Meine ursprüngliche Absicht war es, eine Liste von Zeilen mit einem Null-Eintrag zu erhalten und einfach in der Ergebnisliste nach unten zu scrollen und mit meinen Augen nach solchen zu suchen, die die kleinen gelben Null-Rechtecke noch nicht haben und im Schema ohnehin als nullbar markiert sind .
IBC
Ich denke also, was ich wirklich will, ist eine Liste von Spalten, die als nullbar markiert sind, obwohl keine Datensätze eine Null für diese Spalte enthalten.
IBC

Antworten:

8

Die Antwort von Martin Smith wird sehr gut dazu dienen, Ihnen alle Spalten zu liefern, die Sie für eine gesamte Datenbank in SQL 2008 benötigen. Sehr schön!

Hier ist, wie ich es in den Tagen gemacht habe, bevor SQL CTEs und PIVOT hatte. Dies ist kompatibel mit älteren SQL-Versionen, bei denen Martins Lösung nicht funktioniert und auch 2008 funktioniert, jedoch mit einer schlechteren Leistung als seine Lösung.

USE MyDB

SET NOCOUNT ON

CREATE TABLE ##nullable  (
    ID INT IDENTITY(1,1),
    SchName VARCHAR(128),
    TblName VARCHAR(128),
    ColName VARCHAR(128),
    hasNulls BIT,
    PRIMARY KEY(ID)
    )

DECLARE @currTbl VARCHAR(128)
DECLARE @currCol VARCHAR(128)
DECLARE @currSch VARCHAR(128)
DECLARE @limit INT
DECLARE @i INT
DECLARE @sql NVARCHAR(4000)

INSERT INTO ##nullable (
    SchName,
    TblName,
    ColName,
    hasNulls
    )
SELECT
    c.TABLE_SCHEMA,
    c.TABLE_NAME,
    c.COLUMN_NAME,
    0 AS hasNulls
FROM INFORMATION_SCHEMA.COLUMNS c
INNER JOIN INFORMATION_SCHEMA.TABLES t
ON c.TABLE_CATALOG = t.TABLE_CATALOG
AND c.TABLE_NAME = t.TABLE_NAME
AND c.TABLE_SCHEMA = t.TABLE_SCHEMA
WHERE c.IS_NULLABLE = 'YES'
AND t.TABLE_TYPE = 'BASE TABLE'

SET @limit = (SELECT MAX(ID) FROM ##nullable)
SET @i = 1

WHILE @i <= @limit
BEGIN   
    SELECT @currSch = SchName,
        @currTbl = TblName,
        @currCol = ColName
    FROM ##nullable 
    WHERE ID = @i

    SET @sql = 'UPDATE ##nullable 
        SET hasNulls = 1
        WHERE ID = ' + CAST(@i AS VARCHAR(20)) + ' 
        AND EXISTS (SELECT 1 FROM ' + QUOTENAME(@currSch) + '.'+ QUOTENAME(@currTbl) + ' 
        WHERE ' + QUOTENAME(@currCol) + ' IS NULL)'

    EXEC(@sql)

    SET @i = @i + 1     
END

SELECT DISTINCT * FROM ##nullable 
WHERE hasNulls = 0  

DROP TABLE ##nullable
Jonathan Van Matre
quelle
Gute Antwort. Sie haben am Ende sogar den temporären Tisch fallen lassen, damit er mich beim zweiten Mal nicht beschimpft.
IBC
Scheint auch sehr elegant.
IBC
Ich habe das aus meinem Kopf heraus neu erstellt und festgestellt, dass es einen Teil davon gibt, den ich vergessen habe, altmodisch zu sein. Bearbeitet, um die Verwendung von sys zu ersetzen. Ansichten mit INFORMATION_SCHEMA für eine bessere Abwärtskompatibilität.
Jonathan Van Matre
1
+1 Bedeutet mehrere Abfragen für dieselbe Tabelle, kann jedoch kurzschließen, wenn a NULLgefunden wird, ohne dass die gesamte Tabelle gescannt werden muss. Übrigens ist es besser, QUOTENAMEdie eckigen Klammern selbst zu verwenden, als sie zu verketten, da dies automatisch Objektnamen behandelt, die das ]Zeichen enthalten.
Martin Smith
Ja, für SQL 2008 ist Ihr Ansatz weitaus besser optimiert. Sehr elegant, und ich stecke es in meinen Köcher. Und ein guter Fang für den QUOTENAME Edge Case! Code entsprechend überarbeitet.
Jonathan Van Matre
7

Sie können verwenden sys.sp_MSforeachtable, um alle Tabellen zu durchlaufen und wie folgt zu verarbeiten (möglicherweise müssen Anpassungen vorgenommen werden, wenn Sie nicht standardmäßige Objektnamen haben, die das 'Zeichen enthalten, aber ich werde dies als Übung belassen.)

CREATE TABLE #Results
(
object_name nvarchar(500),
column_name  nvarchar(500)
)

exec sys.sp_MSforeachtable '
IF EXISTS(
SELECT * FROM sys.columns WHERE object_id = OBJECT_ID(''?'') AND is_nullable=1)
BEGIN
RAISERROR(''Processing ?'',0,1) WITH NOWAIT


DECLARE @ColList nvarchar(max)
DECLARE @CountList nvarchar(max)

SELECT @ColList = ISNULL(@ColList + '','','''') + QUOTENAME(name),
       @CountList = ISNULL(@CountList + '','','''') + ''COUNT(*) 
          - COUNT(CASE WHEN '' + QUOTENAME(name) + '' IS NOT NULL THEN 1 END) AS '' + QUOTENAME(name)
FROM sys.columns WHERE object_id = OBJECT_ID(''?'')  AND is_nullable=1 

DECLARE @dynsql nvarchar(max)

SET @dynsql = ''
WITH T AS
(
SELECT 
        ''''?'''' AS table_name, 
        '' + @CountList + ''
FROM ?
)
INSERT INTO #Results
SELECT  table_name, col
FROM T
UNPIVOT (NullCount FOR col IN ('' + @ColList + '')) AS UnPvt
WHERE NullCount = 0
''

EXEC(@dynsql)
END
'


SELECT *
FROM #Results

DROP TABLE #Results
Martin Smith
quelle
Lol @ lass es als Übung! Wie auch immer, dies scheint die Logik zu enthalten, die ich will. Ich werde sehen, ob ich es zum Laufen bringen kann.
IBC
@IBC - Haben Sie tatsächlich Tabellennamen oder Spaltennamen, die das 'Zeichen enthalten?
Martin Smith
+1 für die Eleganz der PIVOT-Lösung und für die vollständige Lösung des Problems
Jonathan Van Matre
Ich glaube nicht, dass meine Tabellennamen '
IBC
Ich habe gerade festgestellt, dass ich es in einer kleineren Datenbank testen muss. Die Ausführung auf der Hauptdatenbank kann eine Stunde dauern.
IBC
2

Vielleicht hilft diese StackOverflow-Antwort: /programming/63291/sql-select-columns-with-null-values-only

Es sieht so aus, als ob es für 2005 geplant ist, hoffe es funktioniert.

Wie gewünscht erweitert (glaube ich):

declare @col varchar(255), @cmd varchar(max)

DECLARE getinfo cursor for
SELECT c.name FROM sys.tables t JOIN sys.columns c ON t.Object_ID = c.Object_ID
WHERE t.Name = 'ADDR_Address'

OPEN getinfo

FETCH NEXT FROM getinfo into @col

WHILE @@FETCH_STATUS = 0
BEGIN
    SELECT @cmd = 'IF NOT EXISTS (SELECT top 1 * FROM ADDR_Address WHERE [' + @col + '] IS NOT NULL) BEGIN print ''' + @col + ''' end'
    EXEC(@cmd)

    FETCH NEXT FROM getinfo into @col
END

CLOSE getinfo
DEALLOCATE getinfo

Ersetzen Sie ADDR_Address durch Ihren Tabellennamen.

Tim
quelle
2

In SQL Server werden einige Systemansichten speziell zur Beschreibung von Objektdefinitionen verwendet: INFORMATION_SCHEMA.Tables, Columns usw.

In diesem Fall benötigen Sie etwa die folgende Abfrage, um nur die letzte Frage zu beantworten ("Liste der Spaltennamen, die in ihrem Schema als nullwertfähig markiert sind ..."):

use DBTest; GO;

SELECT
    c.TABLE_CATALOG as DatabaseName,
    c.TABLE_SCHEMA as SchemaName,
    c.TABLE_NAME as TableName,
    c.COLUMN_NAME as ColumnName,
    c.IS_NULLABLE as IsNullable 
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE c.TABLE_CATALOG = 'DbTest'
and c.TABLE_SCHEMA = 'dbo'
AND c.TABLE_NAME = 'TableTest'
and c.IS_NULLABLE = 'YES'

Dadurch erhalten Sie eine Liste mit Spalten für eine bestimmte Datenbank und Tabelle. Entfernen Sie den Tabellenfilter und Sie erhalten das gewünschte Ergebnis für die gesamte Datenbank. Die INFORMATION_SCHEMA-Ansichten beziehen sich nur auf die aktuell verwendete Datenbank. Sie können sie daher nicht für alle Datenbankdaten gleichzeitig verwenden, es sei denn, Sie führen eine dynamische SQL aus.

Marian
quelle
Dies ist hilfreich, aber ich muss die Liste, die zurückgegeben wird, nur in die Spalten filtern, die nullwertfähig sind UND keine Datensätze in der Datenbank haben, in denen diese Spalte einen Nullwert enthält.
IBC
Ich bin nur einem Teil Ihrer Frage gefolgt, sorry :-). Dann ist es obligatorisch, ein dynamisches SQL zu erstellen, um diese Liste von Tabellen und Spalten zu durchsuchen und alle Zeilen der Tabelle nach dieser Spalte zu summieren und schließlich nur diejenigen anzuzeigen, deren Summe nicht null ist.
Marian