2016-06-16 35 views
5

Przedmówię to, dając wam wszystkim znać, że obiecałem sobie kilka lat temu, że nie będę używał kursora w SQL, gdzie nie jest potrzebny. Niestety myślę, że będę musiał użyć jednego w mojej obecnej sytuacji, ale to było tak długie, że staram się zapamiętać poprawną składnię.Kursor SQL do używania nazw tabel i pól z tabeli Temp

Zasadniczo mam problem z CONVERT_IMPLICIT dzieje się w kwerendach, ponieważ mam typy danych, które są różne dla tego samego pola w różnych tabelach, więc chciałbym ostatecznie przekonwertować te do int. Ale aby to zrobić, muszę sprawdzić, czy wszystkie dane można przekonwertować na int, czy nie, aby zobaczyć, jak duże jest to zadanie.

Mam poniższe zapytanie, które daje mi listę wszystkich tabel w bazie danych, które zawierają odpowiednie pole na liście;

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' 

Co daje takie wyniki;

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 

Co chciałbym zrobić, to sprawdzić, czy wszystkie dane w odpowiedniej tabeli & dziedzinie mogą być konwertowane do int i aktualizować „cabrio” pole (1 czy jest dane, które nie mogą być zamienione , 0, jeśli dane są w porządku). Mam następujące obliczenia, które działa idealnie dobrze;

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

I daje wynik, że jestem po. Ale staram się uzyskać poprawną składnię, aby utworzyć kursor, który będzie wyglądać w każdym wierszu w mojej tabeli tymczasowej i odpowiednio uruchomić ten SQL. Następnie musi zaktualizować końcową kolumnę tabeli tymczasowej z wynikiem zapytania (1 lub 0).

To będzie trzeba uruchomić na kilkuset bazach danych, dlatego potrzebuję tej listy, aby była dynamiczna, w niektórych bazach danych mogą być również niestandardowe tabele (w rzeczywistości jest to całkiem prawdopodobne).

Jeśli ktokolwiek mógłby udzielić wskazówek, byłoby to bardzo cenne.

Dzięki

Odpowiedz

2

Wprowadziłem kilka zmian do pierwotnego zapytania, ale tutaj jest coś, co powinno działać. Zrobiłem podobne rzeczy w przeszłości :-)

Zmiany:

  • Dodany schemat do tabeli źródłowej - moja baza Test miał mecze w wielu schematów
  • Zmienione datatypes do sysName, smallint, aby dopasować definicje tabeli lub nazwy mógł się obcięty

    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

Naprawdę nie jest częścią odpowiedzi, ale czułem, że twoja definicja Convertible i twoje zapytanie o definicję cabrio były być może sprzeczne ze sobą i używając twojego zapytania kolumna końcowa mogłaby być nazwana Not Convertible ... – SMM

+0

Jesteś absolutnym świętym . Tak, teraz logika jest tutaj, mogę grać i przejść do następnego kroku z tym. Doceniane, dziękuję. Muszę poczekać do jutra przed przyznaniem nagrody. –

1

Jeśli dobrze rozumiem pytanie, chciałbym zrobić coś takiego, aby zidentyfikować te rekordy, które nie rzucają jako [int].

Nie podano, z której wersji SQL Server korzystasz; TRY_CAST i TRY_CONVERT to rok 2012 lub nowszy.

DECLARE @test AS TABLE ([field] [sysname]);

WKŁADKA DO @test ([pola]) WARTOŚCI (N'1') (n'a”);

SELECT [pole] FROM @test GDZIE TRY_CAST ([pole] AS [INT]) JEST NULL;

- jest to podstawowa składnia SQL dla kursora kursor (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

Gdyby powiedział, przeprosiny. Jestem na SQL 2014, ale klienci mogą być na wszystkich z powrotem do SQL 2008. Nie jest to obliczenie, które mnie interesuje w szczególności, jak go wywołać raz z każdego wiersza w mojej tabeli tymczasowej. –

1

nie byłem w stanie przetestować ale powinien robić to, co szukasz.Po prostu zapisz to po utworzeniu tabeli temp:

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

to nie zadziałało, obawiam się. Pracowałem nad tym i wyłączałem, ale po prostu nie mogę go uruchomić poprawnie. –