2016-06-26 53 views
9

Ok, 4 ore di codifica e solo 6 ore di ricerca ... e non sto meglio di quando ho iniziato. Ecco il mio problema Ho una tabella (tmpShell) e ha 12 colonne. È una tabella di base senza vincoli, utilizzata per la segnalazione temporanea. Mentre inseriamo i dati, devo estrarre un numero ID (PatientId) e tutti i nomi delle colonne dove il valore per quel PatientId è nullo.Selezione dei nomi di colonna in cui i valori sono NULL

Esempio:

 
PatientId    Fname    Lname      DOB 
123455       Sam      NULL       NULL
2345455      NULL     Doe        1/1/1980
09172349     John     Jone       NULL

Quello che voglio tornare è:

 
PatientId    ErrorMsg 
123455       Lname,DOB
2345455      Fname
09172349     DOB

Naturalmente, se tutte le colonne hanno un valore, l'ErrorMsg sarebbe nullo.

Ho provato e fallito circa 300 diversi pezzi di codice, ma questo sembra essere il più vicino possibile. Sfortunatamente, questo restituisce solo OGNI colonna, non i null.

 
    ALTER PROC [sp_aaShowAllNullColumns] 
     @tableName VARCHAR(255) 
    AS 
     BEGIN 
      SET NOCOUNT ON;

DECLARE @sql NVARCHAR(4000); DECLARE @cols NVARCHAR(4000); DECLARE @tcols TABLE ([colbit] NVARCHAR(255)); --DECLARE @tablename VARCHAR(255) = 'tmpShell'; INSERT @tcols SELECT 'count(' + [columns].[name] + ') as ' + [columns].[name] + ', ' AS [colbit] FROM [sys].[columns] WHERE [columns].[object_id] = OBJECT_ID(@tableName); SELECT @cols = COALESCE(@cols, ', ', '') + [@tcols].[colbit] FROM @tcols; SELECT @cols = SUBSTRING(@cols, 1, (LEN(@cols) - 1)); SELECT @cols = ISNULL(@cols, ''); SELECT @sql = 'select patientid, count(*) as Rows' + @cols + ' from ' + @tableName + ' group by patientid having count(*) > 0'; CREATE TABLE [tmpShell2] ( [patientid] VARCHAR(15) ,[Rows] CHAR(2) ,[Rn] CHAR(2) ,[patId] CHAR(2) ,[fname] CHAR(2) ,[lname] CHAR(2) ,[dob] CHAR(2) ,[addr1] CHAR(2) ,[city] CHAR(2) ,[state] CHAR(2) ,[zip] CHAR(2) ,[country] CHAR(2) ,[psite] CHAR(2) ,[csite] CHAR(2) ,[ssite] CHAR(2) ,[scode] CHAR(2) ,[sfid] CHAR(2) ,[taskid] CHAR(2) ,[errormsg] CHAR(2) ); INSERT INTO [tmpShell2] EXEC [sys].[sp_executesql] @sql; DECLARE @tbl VARCHAR(255) = 'tmpShell2'; SELECT DISTINCT [TS].[patientid] , STUFF(( SELECT DISTINCT ', ' + [C].[name] FROM [tmpShell2] AS [TS2] JOIN [sys].[columns] AS [C] ON [C].[object_id] = OBJECT_ID(@tbl) WHERE [C].[name] NOT IN ('SFID', 'TaskId', 'ErrorMsg') AND [C].[name] IS NOT NULL FOR XML PATH('') ), 1, 1, '') FROM [tmpShell2] AS [TS]; DROP TABLE [dbo].[tmpShell2]; END; GO EXEC [sp_aaShowAllNullColumns] 'tmpShell'; </pre>
+0

Devi farlo su un numero sconosciuto di tavoli o solo su questo? –

+0

Qual è la probabilità che le colonne (diverse dalla chiave) in questa tabella cambino, o che ci siano colonne dove non ti interessa se c'è un nullo? –

+0

Sì, il numero di tabelle è sconosciuto e le colonne variano da tabella a tabella. La probabilità che qualcosa cambierà è molto sottile – RazorSharp

risposta

3

Che ne dite di qualcosa di simile?

SELECT 
a.PatientID 
, CASE a.tmpCol 
    WHEN '' THEN NULL 
    ELSE STUFF(a.tmpCol,1,1,'') 
END AS ErrorMsg 
FROM 
(
SELECT 
    PatientID 
    , CASE WHEN FirstName IS NULL THEN ',FirstName' ELSE '' END 
    + CASE WHEN LastName IS NULL THEN ',LastName' ELSE '' END 
    + CASE WHEN DOB IS NULL THEN ',DOB' ELSE '' END AS tmpCol 
FROM 
    <tableName> 
) a; 
+0

Sto usando questo per ora, ma mi piacerebbe un modo più dinamico che è flessibile per varie tabelle e nomi di colonne. – RazorSharp

+0

Ho aggiunto una risposta qui sotto che credo sia flessibile per tabelle e nomi di colonne. – DVT

8

Ti penso a cose complicate.

Puoi provare a utilizzare CASE EXPRESSION:

SELECT t.patientID, 
     CASE WHEN t.fname is NULL THEN 'Fname,' ELSE '' END + 
     CASE WHEN t.Lname is NULL THEN 'Lname,' ELSE '' END + 
     CASE WHEN t.DOB is NULL THEN 'DOB,' ELSE '' END 
     ..... as ErrorMsg 
FROM YourTable t 

questo si tradurrà con una virgola inutile, alla fine del errorMsg, di gestire la cosa si può fare questo:

 REPLACE(CASE... + 
       CASE... + 
       CASE WHEN t.DOB is NULL THEN 'DOB,' ELSE '' END 
       ..... + ' ') ', ','') as ErrorMsg 

Questo renderà il l'ultima virgola è univoca perché avrà uno spazio concatenato e farà in modo che solo essa venga cancellata.

+0

Come pensate di gestire il caso in cui solo "Fname' appare per un dato record? Quindi avrà una virgola penzolante. –

+0

@TimBiegeleisen In questo modo .. – sagi

2

È possibile utilizzare una query pivot per ottenere il risultato desiderato. La seguente query dovrebbe approssimare l'output desiderato:

SELECT PatientId, 
    CONCAT(CASE WHEN Fname IS NULL THEN 'Fname ' ELSE '' END, 
      CASE WHEN Lname IS NULL THEN 'Lname ' ELSE '' END, 
      CASE WHEN DOB IS NULL THEN 'DOB' ELSE '' END) 
FROM yourTable 
+0

Voleva la lista con virgole, 14 colonne senza una virgola non saranno leggibili. – sagi

1

Ecco uno SQLfiddle basata su https://stackoverflow.com/a/38036046/2314737 con la gestione delle virgole

http://sqlfiddle.com/#!9/708796/1

SELECT PatientId, 
    REPLACE(RTRIM(CONCAT(CASE WHEN Fname IS NULL THEN 'Fname ' ELSE '' END, 
      CASE WHEN Lname IS NULL THEN 'Lname ' ELSE '' END, 
      CASE WHEN DOB IS NULL THEN 'DOB' ELSE '' END)), 
      ' ',',') 
FROM YourTable 
+0

hi @sagi grazie per il tuo feedback. Quando tutti i campi non sono NULL ci sarà uno spazio vuoto, immagino che sia necessario fare un'altra selezione su questa selezione per escludere le righe con spazi vuoti. – user2314737

+0

@sagi non si occupa di RTRIM prima di REPLACE? – user2314737

+0

Sì, mi sono persa che mi dispiace. – sagi

0

Questa è una stored procedure che prenderà il nome dello schema, il nome della tabella e la colonna nome, quindi produrre il risultato desiderato.

ALTER PROCEDURE [dbo].[DynamicErrorProducing] 
@schema_name NVARCHAR(100), 
@table_name NVARCHAR(100), 
@column_name NVARCHAR(100) 
AS 
BEGIN 
DECLARE @countTable TINYINT; 
DECLARE @countColumn TINYINT; 
DECLARE @string NVARCHAR(MAX); 
DECLARE @sqlString NVARCHAR(MAX); 

SET @countTable=0; 

SELECT 
    @countTable=COUNT(*) 
FROM 
    sys.tables 
WHERE 
    SCHEMA_NAME(schema_id)[email protected]_name 
    AND [email protected]_name; 

-- If there is no table as described, quit with an error 
IF (@countTable = 0) 
    RETURN 0; 

SET @countColumn=0; 

SELECT 
    @countColumn=COUNT(*) 
FROM 
    sys.columns 
WHERE 
    object_id = OBJECT_ID(@schema_name+'.'[email protected]_name) 
    AND [email protected]_name; 

IF (@countColumn<> 1) 
    RETURN; 

SELECT 
    @countColumn=COUNT(*) 
FROM 
    sys.columns 
WHERE 
    object_id = OBJECT_ID(@schema_name+'.'[email protected]_name) 
    AND name<>@column_name; 

IF (@countColumn<1) 
    RETURN; 

SELECT @string=STUFF(
    (
    SELECT 
     '+ CASE WHEN '+name+' IS NULL THEN '''' ELSE '','+name+''' END ' 
    FROM 
     sys.columns 
    WHERE 
     object_id = OBJECT_ID(@schema_name+'.'[email protected]_name) 
     AND name<>@column_name 
    FOR XML PATH('')) 
    ,1,2,''); 


SET @sqlString=N' 
SELECT 
    a.'[email protected]_name+' 
    , CASE WHEN a.tmpCol='''' THEN NULL ELSE STUFF(a.tmpCol,1,1,'''') END AS ErrMsg 
FROM 
(
SELECT 
    '[email protected]_name+' 
    ,'[email protected]+' AS tmpCol 
FROM 
    '[email protected]_name+'.'[email protected]_name+' 
) a'; 

EXEC sp_executesql 
    @[email protected]; 

END 


GO