2015-09-28 48 views
1

我有一個INSERT語句看起來像這樣:如何識別列(S)負責

INSERT INTO CLIENT_TABLE 
SELECT NAME, SURNAME, AGE FROM CONTACT_TABLE 

我上面的例子是一個基本的,但有沒有辦法在SELECT語句中傳遞,然後根據實際字段的大小檢查返回的列值?

對每一列檢查LEN是不實用的。我正在尋找自動化的東西。

+0

爲什麼要檢查長度?在INSERT INTO CLIENT_TABLE SELECT LEFT(NAME,30),LEFT(SURNAME,50),AGE FROM CONTACT_TABLE' –

+0

我在互聯網上找到了幾種解決方案,但我還沒有嘗試過其中任何一種: http:// raresql.com/2014/01/03/sql-server-a-quick-solution-to-string-or-binary-data-would-be-truncated-using-stored-procedure/, http:// blogs。 lessthandot.com/index.php/datamgmt/datadesign/how-to-find-what-c​​olumn/ 是你在找什麼? –

+0

@PriceCheaperton:如果我沒有弄錯,沒有辦法自動找到SQL Server,但沒有直接根據查詢找到截斷字符串的功能。你真的需要額外的時間來檢查調試。我認爲重要的是如何輕鬆調試並找到截斷字符串的原因。 – Japongskie

回答

2

我在那樣的問題調試..

我刪除列在SELECT一個接一個,如果沒有返回錯誤,那麼你知道什麼是列截斷問題的原因..但在這裏是一些關於調試的提示。

  • 選項1:與容納更多字符列第一啓動..喜歡VARCHAR,例如在你的情況,我認爲列NAME, SURNAME是一個導致一個錯誤,因爲AGE列不持有多字符,因爲它的整數。你應該調試那樣的東西。

  • 選項2:您可以調查最終輸出中的列。最後的SELECT將返回所有列及其值,然後您可以檢查這些值是否與您在UI上輸入的內容匹配。

    Ex。看到預期與實際輸出結果下面的圖片

    預計:

    enter image description here

    實際輸出:

    enter image description here

    我的例子在選項2表明,截斷的字符串是SURNAME你可以看到..

    注:您只能使用選項2,如果查詢未返回執行錯誤,意思是說,截斷的字符串沒有返回錯誤但是創建了一個我們不想要的意外拆分字符串。

    IF查詢返回一個錯誤,你最好的選擇是選擇1,消耗更多的時間,但值得,因爲這是確保最好的方式,你發現導致截斷問題的確切的列

那麼如果你已經找到導致問題的列,你現在可以調整列的大小,或者另一種方法是限制用戶的輸入,你可以對用戶進行一些驗證以避免截斷問題,但取決於您的要求,您希望程序如何工作完全取決於您。

我的回答/建議是基於我在這種情況下的經驗。

希望這個答案能幫到你。 :)

+0

謝謝,但檢查列反對每列不實用。我正在尋找自動化的東西。 – PriceCheaperton

+0

@PriceCheaperton,這是找到截斷字符串的最佳方式:),但如果您想要另一種方式,則取決於您。 – Japongskie

1

檢查每個字段的最大長度,這樣您可以確定超出表中指定的字符數限制的字段,例如CLIENT_TABLE

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

比較Client_Table設計 喜歡的結果,如果在Client_Table「名稱」爲Varchar(50)類型和驗證查詢(上面寫的),比「名」返回超過50個字符的字段造成超過流量。

+0

謝謝,但檢查列對每列不實際。我正在尋找自動化的東西。 – PriceCheaperton

1

有一個偉大的答案被阿龍貝特朗的問題: Retrieve column definition for stored procedure result set

如果您使用的SQL Server 2012+你可以使用sys.dm_exec_describe_first_result_set。這裏有一個nice article的例子。但是,即使在SQL Server 2008中,也可以檢索查詢的列類型。亞倫的回答詳細解釋了它。

事實上,在你的情況下,它更容易,因爲你有一個SELECT聲明,你可以複製粘貼,而不是隱藏在存儲過程中的東西。我假設你的SELECT是一個複雜的查詢,從很多表中返回列。如果它只是一張桌子,你可以直接使用sys.columns

因此,創建一個基於您的複雜SELECT#tmp1表:

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

創建基於您的複雜SELECT的目的地的第二#tmp2表:

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

注意,我們不要不需要任何行,只需要元數據列,所以TOP(0)非常方便。 一旦這些#tmp表存在,我們可以使用sys.columns查詢自己的元數據,並比較一下:

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 
; 

另一種可能的方法來比較:

WITH 

... as above ... 

SELECT * FROM CTE1 

EXCEPT 

SELECT * FROM CTE2 
; 

最後

DROP TABLE #tmp1; 
DROP TABLE #tmp2; 

你可以調整比較以適合您的需求。

0

如果您使用SQL Server Manager Studio(SSMS),手動解決方案非常快速。首先捕捉你的SELECT語句的表結構到工作表:

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

然後,右鍵單擊您原來的目標表(CLIENT_TABLE)和腳本它作爲創建一個新的SSMS的窗口。然後右鍵單擊您的工作表(zz_CONTACT_TABLE)並將此表的創建腳本編寫到第二個SSMS窗口。將兩個窗口並排排列,並根據CLIENT_TABLE檢查zz_CONTACT_TABLE的列。即使有數百個輸出列,也會立即看到長度和亂序列的差異。

最後放下你的工作表:

DROP TABLE dbo.zz_CONTACT_TABLE; 

關於自動化解決方案,它是很難看到這是如何工作的。基本上,您正在比較目標表(或目標表中的列的子集)與SELECT語句的輸出。我想你可以編寫一個帶有兩個varchar參數的存儲過程:目標表名和將填充它的SELECT語句。但是,這並不能處理僅填充目的地的一些列的情況,並且這將比上面的手動解決方案做更多的工作。

0

這裏有一些代碼來比較兩行產生SQL語句來比較列。它使用服務器名稱,數據庫名稱和T-SQL查詢指定的兩個行集作爲參數。它可以比較不同數據庫中的數據甚至不同SQL Server上的數據。

--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 

您可以調整finally選擇語句以突出顯示您希望的任何差異。你也可以把它包裝在一個過程中,如果你願意的話,爲它製作一個漂亮的小用戶界面,這樣它就可以切割並粘貼到快速結果上。