/ / Jak zidentyfikować kolumny odpowiedzialne za „Dane ciągowe lub binarne zostaną obcięte”. - sql, sql-server, sql-server-2008

Jak zidentyfikować kolumny odpowiedzialne za „Łańcuch lub dane binarne zostałyby obcięte”. - sql, sql-server, sql-server-2008

Mam instrukcję INSERT, która wygląda następująco:

INSERT INTO CLIENT_TABLE
SELECT NAME, SURNAME, AGE FROM CONTACT_TABLE

Mój powyższy przykład jest podstawowym, ale czy istnieje sposób na przekazanie instrukcji SELECT, a następnie sprawdzenie zwróconych wartości kolumn z rzeczywistymi rozmiarami pól?

Sprawdzanie LEN w każdej kolumnie nie jest praktyczne. Szukam czegoś, co jest zautomatyzowane.

Odpowiedzi:

2 dla odpowiedzi № 1

Moje debugowanie w tego rodzaju problemach to ...

Usuwam kolumny w SELECT jeden po drugim, jeśli nie zwrócił błędu, to wiesz, która kolumna jest przyczyną problemu z obcięciem ... ale oto kilka wskazówek dotyczących debugowania.

  • Opcja 1: Zacznij od kolumn, które mają więcej znaków ... jak VARCHAR, na przykład w twoim przypadku, myślę, że kolumna NAME, SURNAME są przyczyną błędu, ponieważ AGE kolumna nie zawiera wielu znaków, ponieważ jest liczbą całkowitą. Powinieneś debugować coś takiego.

  • Opcja 2: Możesz zbadać kolumnę w końcowym wyniku. Finał SELECT zwróci wszystkie kolumny i ich wartości, a następnie możesz sprawdzić, czy wartości są zgodne z tym, co wprowadziłeś w interfejsie użytkownika itp.

    Dawny. Zobacz Wyniki oczekiwane a rzeczywiste wynik na obrazku poniżej

    Spodziewany:

    wprowadź opis obrazu tutaj

    Rzeczywisty wynik:

    wprowadź opis obrazu tutaj

    Mój przykład w opcji 2 pokazuje, że obcięty ciąg to SURNAME jak widzisz..

    UWAGA: Opcji 2 można użyć tylko wtedy, gdy zapytanie nie zwróciło błędu wykonania, co oznacza, że ​​obcięty ciąg nie zwrócił błędu ALE utworzył nieoczekiwany łańcuch podziału, którego nie chcemy.

    JEŚLI zapytanie zwraca błąd, najlepszym wyborem jest opcja 1, która zajmuje więcej czasu, ale Warto było, ponieważ jest to najlepszy sposób, aby upewnić się, że znalazłeś dokładnie tę kolumnę, która powoduje problem z obcięciem

Następnie, jeśli już znalazłeś kolumny, które powodująproblem, możesz teraz dostosować rozmiar kolumny lub innym sposobem jest ograniczenie wprowadzania danych przez użytkownika?, możesz dać użytkownikom walidację, aby uniknąć problemu z obcięciem, ale wszystko zależy od ciebie, jak chcesz program działa w zależności od Twoich wymagań.

Moje odpowiedzi / sugestie opierają się na moim doświadczeniu w tego rodzaju sytuacjach.

Mam nadzieję, że ta odpowiedź ci pomoże. :)


1 dla odpowiedzi nr 2

Sprawdź maksymalną długość każdego pola, w ten sposób możesz zidentyfikować pola, które przekraczają limit znaków określony w Twojej tabeli, np CLIENT_TABLE.

    SELECT Max(Len(NAME)) MaxNamePossible
, Max(Len(SURNAME)) MaxSurNamePossible
, Max(Len(AGE)) MaxAgePossible
FROM CONTACT_TABLE

Porównaj wynik z projektem tabeli Client_Table Jak gdyby w Client_Table „Nazwa” jest typu Varchar(50) a zapytanie walidacyjne (napisane powyżej) zwraca więcej niż 50 znaków niż pole „Nazwa” powoduje przepełnienie.


1 dla odpowiedzi nr 3

Aaron Bertrand ma świetną odpowiedź na pytanie: Pobierz definicję kolumny dla zestawu wyników procedury składowanej

Jeśli korzystałeś z SQL Server 2012+, możesz użyć sys.dm_exec_describe_first_result_set. Tutaj jest fajny artykuł z przykładami. Ale nawet w SQL Server 2008 można pobrać typy kolumn zapytania. Odpowiedź Aarona wyjaśnia to szczegółowo.

W rzeczywistości w twoim przypadku jest to łatwiejsze, ponieważ masz plik SELECT oświadczenie, że można skopiować i wkleić, a nie coś, co jest ukryte w procedurze składowanej. Zakładam, że twój SELECT to złożone zapytanie zwracające kolumny z wielu tabel. Gdyby to był tylko jeden stół, którego można by użyć sys.columns bezpośrednio z tym stołem.

Stwórz więc pusty plik #tmp1 tabela na podstawie Twojego kompleksu SELECT:

SELECT TOP(0)
NAME, SURNAME, AGE
INTO #tmp1
FROM CONTACT_TABLE;

Utwórz sekundę #tmp2 tabela na podstawie przeznaczenia Twojego kompleksu SELECT:

SELECT TOP(0)
NAME, SURNAME, AGE
INTO #tmp2
FROM CLIENT_TABLE;

Zauważ, że nie potrzebujemy żadnych wierszy, tylko kolumny dla metadanych, więc TOP(0) jest poręczny. Kiedyś te #tmp istnieją tabele, możemy zapytać o ich metadane za pomocą sys.columns i porównaj:

WITH
CTE1
AS
(
SELECT
c.name AS ColumnName
,t.name AS TypeName
,c.max_length
,c.[precision]
,c.scale
FROM
tempdb.sys.columns AS c
INNER JOIN tempdb.sys.types AS t ON
c.system_type_id = t.system_type_id
AND c.user_type_id = t.user_type_id
WHERE
c.[object_id] = OBJECT_ID("tempdb.dbo.#tmp1")
)
,CTE2
AS
(
SELECT
c.name AS ColumnName
,t.name AS TypeName
,c.max_length
,c.[precision]
,c.scale
FROM
tempdb.sys.columns AS c
INNER JOIN tempdb.sys.types AS t ON
c.system_type_id = t.system_type_id
AND c.user_type_id = t.user_type_id
WHERE
c.[object_id] = OBJECT_ID("tempdb.dbo.#tmp2")
)
SELECT *
FROM
CTE1
FULL JOIN CTE2 ON CTE1.ColumnName = CTE2.ColumnName
WHERE
CTE1.TypeName <> CTE2.TypeName
OR CTE1.max_length <> CTE2.max_length
OR CTE1.[precision] <> CTE2.[precision]
OR CTE1.scale <> CTE2.scale
;

Inny możliwy sposób porównania:

WITH

... as above ...

SELECT * FROM CTE1

EXCEPT

SELECT * FROM CTE2
;

Wreszcie

DROP TABLE #tmp1;
DROP TABLE #tmp2;

Możesz dostosować porównanie do swoich potrzeb.


0 dla odpowiedzi nr 4

Ręczne rozwiązanie jest bardzo szybkie, jeśli używasz programu SQL Server Manager Studio (SSMS). Najpierw zapisz strukturę tabeli instrukcji SELECT do tabeli roboczej:

SELECT COL1, COL2, ... COL99 INTO dbo.zz_CONTACT_TABLE
FROM CONTACT_TABLE WHERE 1=0;

Następnie w programie SSMS kliknij prawym przyciskiem myszy oryginałtabela docelowa (CLIENT_TABLE) i skryptu, jak utworzyć w nowym oknie SSMS. Następnie kliknij prawym przyciskiem myszy tabelę roboczą (zz_CONTACT_TABLE) i skryptem tworzenie tej tabeli przejdź do drugiego okna SSMS. Ułóż oba okna obok siebie i sprawdź kolumny zz_CONTACT_TABLE względem CLIENT_TABLE. Różnice w długości i niezgodne z kolejnością kolumny będą natychmiast widoczne, nawet jeśli istnieją setki kolumn wyjściowych.

Na koniec upuść swój stół roboczy:

DROP TABLE dbo.zz_CONTACT_TABLE;

Jeśli chodzi o zautomatyzowane rozwiązanie, jest to trudneżeby zobaczyć, jak to może zadziałać. Zasadniczo porównujesz tabelę docelową (lub podzbiór kolumn w tabeli docelowej) z danymi wyjściowymi instrukcji SELECT. Przypuszczam, że można by napisać procedurę składowaną, która przyjmuje dwa parametry varchar: nazwę tabeli docelowej i instrukcję SELECT, która ją zapełni. Nie rozwiązałoby to jednak przypadku, w którym wypełniane są tylko niektóre kolumny miejsca docelowego, a wymagałoby to więcej pracy niż powyższe ręczne rozwiązanie.


0 dla odpowiedzi № 5

Oto kod do porównania tworzenia dwóch wierszyInstrukcje SQL do porównania kolumn. Jako parametry przyjmuje dwa zestawy wierszy określone za pomocą nazwy serwera, nazwy bazy danych i zapytania T-SQL. Może porównywać dane w różnych bazach danych, a nawet na różnych serwerach SQL.

--setup parameters
declare @Server1 as varchar(128)
declare @Database1 as varchar(128)
declare @Query1 as varchar(max)
declare @Server2 as varchar(128)
declare @Database2 as varchar(128)
declare @Query2 as varchar(max)
set @Server1 = "(local)"
set @Database1 = "MyDatabase"
set @Query1 = "select * from MyTable" --use a select
set @Server2 = "(local)"
set @Database2 = "MyDatabase2"
set @Query2 = "exec MyTestProcedure...." --or use a procedure

--calculate statement column differences
declare @SQLStatement1 as varchar(max)
declare @SQLStatement2 as varchar(max)

set @Server1 = replace(@Server1,"""","""""")
set @Database1 = replace(@Database1,"""","""""")
set @Query1 = replace(@Query1,"""","""""")
set @Server2 = replace(@Server2,"""","""""")
set @Database2 = replace(@Database2,"""","""""")
set @Query2 = replace(@Query2,"""","""""")

CREATE TABLE #Qry1Columns(
[colorder] [smallint] NULL,
[ColumnName] [sysname] COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[TypeName] [sysname] COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[prec] [smallint] NULL,
[scale] [int] NULL,
[isnullable] [int] NULL,
[collation] [sysname] COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]

CREATE TABLE #Qry2Columns(
[colorder] [smallint] NULL,
[ColumnName] [sysname] COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
[TypeName] [sysname] COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
[prec] [smallint] NULL,
[scale] [int] NULL,
[isnullable] [int] NULL,
[collation] [sysname] COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY]

set @SQLStatement1 =
"SELECT *
INTO #Qry1
FROM OPENROWSET(""SQLNCLI"",
""server=" + @Server1 + ";database=" + @Database1 + ";trusted_connection=yes"",
""select top 0 * from (" + @Query1 + ") qry"")

select colorder, syscolumns.name ColumnName, systypes.name TypeName, syscolumns.prec, syscolumns.scale, syscolumns.isnullable, syscolumns.collation
from tempdb.dbo.syscolumns
join tempdb.dbo.systypes
on syscolumns.xtype = systypes.xtype
where id = OBJECT_ID(""tempdb.dbo.#Qry1"")
order by 1"

insert into #Qry1Columns
exec(@SQLStatement1)


set @SQLStatement2 =
"SELECT *
INTO #Qry1
FROM OPENROWSET(""SQLNCLI"",
""server=" + @Server2 + ";database=" + @Database2 + ";trusted_connection=yes"",
""select top 0 * from (" + @Query2 + ") qry"")

select colorder, syscolumns.name ColumnName, systypes.name TypeName, syscolumns.prec, syscolumns.scale, syscolumns.isnullable, syscolumns.collation
from tempdb.dbo.syscolumns
join tempdb.dbo.systypes
on syscolumns.xtype = systypes.xtype
where id = OBJECT_ID(""tempdb.dbo.#Qry1"")
order by 1"

insert into #Qry2Columns
exec(@SQLStatement2)

select ISNULL( #Qry1Columns.colorder, #Qry2Columns.colorder) ColumnNumber,
#Qry1Columns.ColumnName ColumnName1,
#Qry1Columns.TypeName TypeName1,
#Qry1Columns.prec prec1,
#Qry1Columns.scale scale1,
#Qry1Columns.isnullable isnullable1,
#Qry1Columns.collation collation1,
#Qry2Columns.ColumnName ColumnName2,
#Qry2Columns.TypeName TypeName2,
#Qry2Columns.prec prec2,
#Qry2Columns.scale scale2,
#Qry1Columns.isnullable isnullable2,
#Qry2Columns.collation collation2
from #Qry1Columns
join #Qry2Columns
on #Qry1Columns.colorder=#Qry2Columns.colorder

Ostatnią instrukcję można dostosować dozaznacz wszelkie różnice, które chcesz. Możesz również zawrzeć to w procedurze i zrobić dla niej ładny mały interfejs użytkownika, jeśli chcesz, tak aby było to dosłownie wycinanie i wklejanie, aby uzyskać szybkie wyniki.