2016-06-16 23 views
5

Prefaggerò questo facendoti sapere che mi sono ripromesso alcuni anni fa di non utilizzare mai un cursore in SQL dove non è necessario. Purtroppo penso che potrei doverne usare uno nella mia situazione attuale ma è passato così tanto tempo che non riesco a ricordare la sintassi corretta.Cursore SQL per utilizzare i nomi di tabella e di campo dalla tabella temporanea

Fondamentalmente, ho un problema con CONVERT_IMPLICIT che si verifica nelle query perché ho tipi di dati diversi per lo stesso campo in tabelle diverse, quindi mi piacerebbe alla fine convertirli in int. Ma per fare questo ho bisogno di verificare se tutti i dati possono essere convertiti in int o meno per vedere quanto è grande il lavoro.

Ho la seguente query che mi fornisce un elenco di tutte le tabelle nel database che contengono il campo pertinente in un elenco;

IF OBJECT_ID('tempdb..#BaseData') IS NOT NULL DROP TABLE #BaseData 
GO 
CREATE TABLE #BaseData (Table_Name varchar(100), Field_Name varchar(100), Data_Type_Desc varchar(20), Data_Max_Length int, Convertible bit) 

DECLARE @FieldName varchar(20); SET @FieldName = 'TestFieldName' 

INSERT INTO #BaseData (Table_Name, Field_Name, Data_Type_Desc, Data_Max_Length) 
SELECT 
o.name ,c.name ,t.name ,t.max_length 
FROM sys.columns c 
JOIN sys.types t 
    ON c.user_type_id = t.user_type_id 
JOIN sys.objects o 
    ON c.object_id = o.object_id 
WHERE c.name LIKE '%' + @FieldName + '%' 
    AND o.type_desc = 'USER_TABLE' 

Quale dà risultati come questo;

Table_Name Field_Name  Data_Type_Desc Data_Max_Length Convertible 
Table1  TestFieldName varchar   8000   NULL 
Table2  TestFieldName nvarchar  8000   NULL 
Table3  TestFieldName int    4    NULL 
Table4  TestFieldName varchar   8000   NULL 
Table5  TestFieldName varchar   8000   NULL 

Quello che mi piacerebbe fare è quello di verificare se tutti i dati del relativo campo della tabella & possono essere convertiti in un int e aggiornare il campo 'convertibile' (1 se ci sono dati che non possono essere convertiti , 0 se i dati vanno bene). Ho il seguente calcolo che funziona perfettamente;

'SELECT 
CASE 
    WHEN COUNT(' + @FieldName + ') - SUM(ISNUMERIC(' + @FieldName + ')) > 0 
     THEN 1 
    ELSE 0 
END 
FROM ' + @TableName 

E dà il risultato che sto cercando. Ma sto lottando per ottenere la sintassi corretta per creare il cursore che guarderà ogni riga nella mia tabella temporanea ed eseguirà questo SQL di conseguenza. È quindi necessario aggiornare la colonna finale della tabella temporanea con il risultato della query (1 o 0).

Questo dovrà essere eseguito su un paio di centinaia di database, motivo per cui ho bisogno che questa lista sia dinamica, potrebbero esserci tabelle personalizzate in alcuni database (in effetti, è piuttosto probabile).

Se qualcuno potesse fornire una guida, sarebbe molto apprezzato.

Grazie

risposta

2

ho fatto un paio di modifiche alla query originale ma qui è qualcosa che dovrebbe funzionare. Ho fatto cose simili in passato :-)

Modifiche:

  • Aggiunto schema per la tabella di origine - il mio database di test ha avuto partite in più schemi
  • tipi di dati cambiato in sysName, smallint da abbinare definizioni di tabella o nomi potrebbero ottenere troncato

    IF OBJECT_ID('tempdb..#BaseData') IS NOT NULL DROP TABLE #BaseData; 
    GO 
    
    CREATE TABLE #BaseData (Schema_Name sysname, Table_Name sysname, Field_Name sysname, Data_Type_Desc sysname, Data_Max_Length smallint, Convertible bit); 
    
    DECLARE @FieldName varchar(20); SET @FieldName = 'TestFieldName'; 
    
    INSERT INTO #BaseData (Schema_Name, Table_Name, Field_Name, Data_Type_Desc, Data_Max_Length) 
    SELECT 
    s.name, o.name ,c.name ,t.name ,t.max_length 
    FROM sys.columns c 
    JOIN sys.types t 
        ON c.user_type_id = t.user_type_id 
    JOIN sys.objects o 
        ON c.object_id = o.object_id 
    JOIN sys.schemas s ON o.schema_id=s.schema_id 
    WHERE c.name LIKE '%' + @FieldName + '%' 
        AND o.type_desc = 'USER_TABLE'; 
    
    --select * from #BaseData; 
    
    DECLARE @sName sysname, 
         @tName sysname, 
         @fName sysname, 
         @sql VARCHAR(MAX); 
    
    DECLARE c CURSOR LOCAL FAST_FORWARD FOR 
        SELECT Schema_Name, 
          Table_Name, 
          Field_Name 
        FROM #BaseData; 
    OPEN c; 
    FETCH NEXT FROM c INTO @sName, @tName, @fName; 
    
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
    SET @sql = 'UPDATE #BaseData SET Convertible = 
           (SELECT 
           CASE 
            WHEN COUNT(' + @fName + ') - SUM(ISNUMERIC(' + @fName + ')) > 0 
             THEN 1 
            ELSE 0 
           END Convertible 
           FROM ' + @sName + '.' + @tName + ') 
          FROM #BaseData WHERE Schema_Name = ''' + @sName + ''' AND Table_Name = ''' + @tName + ''' AND Field_Name = ''' + @fName + ''''; 
    
    --select @sql; 
    EXEC(@sql); 
    
    FETCH NEXT FROM c INTO @sName, @tName, @fName; 
    END 
    
    CLOSE c; 
    DEALLOCATE c; 
    
    select * 
    from #BaseData; 
    
+0

Non proprio parte della risposta, ma sembrava che la tua definizione di Convertibile e la tua query per definire convertibili fossero forse in disaccordo tra loro e usando la tua query la colonna finale potrebbe essere chiamata Non Convertibile ... – SMM

+0

Sei un vero santo . Sì, ora la logica è qui su cui posso giocare e andare al prossimo passo con questo. Apprezzato, grazie. Devo aspettare fino a domani prima di assegnare la taglia. –

1

Se ho capito la tua domanda, vorrei fare qualcosa di simile per identificare quei record che non proiettano come un [int].

Non hai indicato quale versione di SQL Server stai utilizzando; TRY_CAST e TRY_CONVERT sono 2012 o successivi.

DECLARE @test COME TABELLA ([campo] [sysname]);

INSERT INTO @test ([campo]) VALORI (N'1'), (N'a');

SELECT [campo] FROM @test DOVE TRY_CAST ([campo] AS [INT]) È NULL;

- Questa è la sintassi SQL di base per un cursore CURSORE (https://msdn.microsoft.com/en-us/library/ms180169.aspx)

DECLARE @parameter [sysname]; 
    BEGIN 
     DECLARE [field_cursor] CURSOR 
     FOR 
       SELECT [value] 
       FROM [<schema>].[<table>]; 

     OPEN [field_cursor]; 

     FETCH NEXT FROM [field_cursor] INTO @parameter; 

     WHILE @@FETCH_STATUS = 0 
       BEGIN 
        -- do something really interesting here 

        FETCH NEXT FROM [field_cursor] INTO @parameter; 
       END; 

     CLOSE [field_cursor]; 

     DEALLOCATE [field_cursor]; 
    END; 
+0

dovuto dire, le scuse. Sono su SQL 2014 ma i clienti possono fare qualsiasi cosa su SQL 2008. Non è il calcolo a cui sono interessato, in particolare, è come chiamarlo una volta da ogni riga della mia tabella temporanea. –

1

non ero in grado di testare questo ma dovrebbe fare quello che stai cercando.Proprio plop questo dopo aver creato la vostra tabella temporanea:

DECLARE @tName VARCHAR(20), 
     @fName VARCHAR(20), 
     @dType VARCHAR(20), 
     @dLength INT, 
     @sql VARCHAR(MAX); 

DECLARE c CURSOR LOCAL FAST_FORWARD FOR 
    SELECT Table_Name, 
      Field_Name, 
      Data_Type_Desc, 
      Data_Max_Length 
    FROM #BaseData; 
OPEN c; 
FETCH NEXT FROM c INTO @tName, @fName, @dType, @dLength; 

WHILE @@FETCH_STATUS = 0 
BEGIN 
    IF((COUNT(@FieldName) - SUM(ISNUMERIC(@FieldName))) > 0) 
    BEGIN 
     SET @sql = 'UPDATE ' + @tName + ' SET Convertible = 1 WHERE Table_Name = ''' + @tName + ''''; 
    END 
    ELSE 
    BEGIN 
     SET @sql = 'UPDATE ' + @tName + ' SET Convertible = 0 WHERE Table_Name = ''' + @tName + ''''; 
    END 

    EXEC(@sql); 

    FETCH NEXT FROM c INTO @TableName, @FieldName, @DataType, @DataLength; 
END 

CLOSE c; 
DEALLOCATE c; 
+0

questo non ha funzionato, ho paura. Ci ho lavorato su e giù ma non riesco a farlo funzionare correttamente. –