我编写一个游标来从表中选择数据到变量。
使用oracle我可以简单地使用单个declare语句,该语句实际上为每个列创建一个变量,其数据类型与基础表的类型相匹配(因为它当前在运行时定义)。
在SQL服务器中,您似乎必须手动为每列声明一个变量。
我是对的。有没有人有任何想法?
我认为与oracle相比,这是一个不喜欢SQL Server的重要原因。
我很失望SS没有%TYPE和%ROWTYPE。这些可以节省最初编写代码时的工作时间,如果需要更改列的类型,则可以省去返回并重新编写所有代码的工作。
在Oracle中我曾经写过类似的东西:
DECLARE @MyRowVar AS ATable%ROWTYPE;
在SQL Server中,我只需写这个:
DECLARE @External_ID AS BIGINT = NULL;
DECLARE @PlatformID AS INT = NULL;
DECLARE @ActorIDOfReseller AS INT = NULL;
DECLARE @ActorIDOfClient AS INT = NULL;
DECLARE @ActorIDOfExtension AS INT = NULL;
DECLARE @CallType AS NCHAR (10) = NULL;
DECLARE @CallInitiatedDate AS DATE = NULL;
DECLARE @CallInitiatedTimeHH24MI AS TIME (0) = NULL;
DECLARE @TimePeriodID AS INT = NULL;
DECLARE @CallAnswered AS DATETIME = NULL;
DECLARE @CallAnsweredYN AS BIT = NULL;
DECLARE @CallDispositionID AS INT = NULL;
DECLARE @CountryID AS INT = NULL;
DECLARE @CallPrefixID AS INT = NULL;
DECLARE @FromNumber AS VARCHAR (32) = NULL;
DECLARE @ToNumber AS VARCHAR (80) = NULL;
DECLARE @CallDuration AS INT = NULL;
DECLARE @CallCostToExtension AS DECIMAL (10, 6) = NULL;
DECLARE @CallCostToClient AS DECIMAL (10, 6) = NULL;
DECLARE @CallCostToReseller AS DECIMAL (10, 6) = NULL;
DECLARE @CallCostToAdmin AS DECIMAL (10, 6) = NULL;
DECLARE @Flow AS VARCHAR (3) = NULL;
DECLARE @CallStart AS DATETIME = NULL;
DECLARE @MoneyUnit AS VARCHAR (32) = NULL;
DECLARE @Prefix AS VARCHAR (32) = NULL;
DECLARE @External_CallID AS VARCHAR (255) = NULL;
这让我很伤心,浪费了我的生命。
我将此标记为Oracle,因为我确信oracle开发人员会想知道他们使用这个专业工具是多么幸运。
哈维
续
从下面的评论看来,有一种更简单的方法!非常感谢您的投入。我咆哮着,因为我有点开始......抱歉。
这里有更多背景信息:
我有一个csv文件,每列都有不可靠的数据。
我BULK将此csv文件加载到tableA中,其中所有列都声明为VARCHAR(100)。 (即使值的数据类型不是列中的预期,文件也会加载)
我使用单个语句将tableA中的数据传输到tableB中。 TableB与tableA具有相同的列,但tableB列具有各种数据类型 - 这是csv数据应具有的预期数据类型。
(TableB还有一些其他未填写的文件列,例如IDENTITY列等。)。
声明如下:
INSERT INTO tableB (列表) 选择 ({select列的列表}) FROM tableB;
上面的{列列表}中的每一列都包含TRY_CAST函数,以将其转换为与目标表相同的数据类型或NULL。
上面的步骤3工作正常,但我无法识别无效行。 所以我决定尝试使用游标并使用与步骤3中相同的select语句,但不包含每列的TRY_CAST。我在整个INSERT语句周围使用了一个TRY / CATCH,它在select语句的每一行上操作,以便在INSERT失败时记录无效行。
我现在写了第4步,但男孩,我希望我发布了一个"我该怎么做?"先问一下这里。我的代码看起来非常垃圾,我已将其发布在下面。
正如您可能已经说过的那样,我没有长时间使用SQL Server,而且我在使用大型数据库时非常生气。
非常欢迎您提供更多的输入。我明天会重写这个!
我喜欢Jermey关于使用下面的SQL创建临时表的建议,但我还没有完全理解这一点!
选择* INTO #tmp 来自mytable 其中1 = 0;
无论如何,这是。
(我现在已经运行了这个,并且需要30秒来处理254,000行, - 其中只有一行是失败的 - 但这次是可以接受的。)
CREATE PROCEDURE [dbo].[Call_Process_FromXoomTalk_UsingCursor]
@PlatformId int
, @StartOfMonthDate as Date = NULL
, @EndOfMonthDate as Date = NULL
, @LimitImportToRowsWithThisCallType nVarchar(max) = NULL
, @LimitImportToRowsWithThisFlow nVarchar(max) = NULL
, @Raise1Print2Both3 as tinyint = 3
AS
BEGIN
DECLARE @msg as nVarchar(max);
DECLARE @RowCount as int = 0;
DECLARE @FETCHSTATUS as int = 0;
DECLARE @CountOfRowFailures AS INT = 0;
DECLARE @CountOfRowSuccesses AS INT = 0;
--------------------------------------------------------------------------------------------------
-- The order any type of the variables declared below
-- MUST match the order of the columns of the CURSOR SELECT statement below and
-- the data type of the underlying columns of the table "Call".
--
-- CALL TABLE COLUMNS:
--
DECLARE @External_ID AS BIGINT = NULL;
DECLARE @PlatformID2 AS INT = NULL;
DECLARE @ActorIDOfReseller AS INT = NULL;
DECLARE @ActorIDOfClient AS INT = NULL;
DECLARE @ActorIDOfExtension AS INT = NULL;
DECLARE @CallType AS NCHAR (10) = NULL;
DECLARE @CallInitiatedDate AS DATE = NULL;
DECLARE @CallInitiatedTimeHH24MI AS TIME (0) = NULL;
DECLARE @TimePeriodID AS INT = NULL;
DECLARE @CallAnswered AS DATETIME = NULL;
DECLARE @CallAnsweredYN AS BIT = NULL;
DECLARE @CallDispositionID AS INT = NULL;
DECLARE @CountryID AS INT = NULL;
DECLARE @CallPrefixID AS INT = NULL;
DECLARE @FromNumber AS VARCHAR (32) = NULL;
DECLARE @ToNumber AS VARCHAR (80) = NULL;
DECLARE @CallDuration AS INT = NULL;
DECLARE @CallCostToExtension AS DECIMAL (10, 6) = NULL;
DECLARE @CallCostToClient AS DECIMAL (10, 6) = NULL;
DECLARE @CallCostToReseller AS DECIMAL (10, 6) = NULL;
DECLARE @CallCostToAdmin AS DECIMAL (10, 6) = NULL;
DECLARE @Flow AS VARCHAR (3) = NULL;
DECLARE @CallStart AS DATETIME = NULL;
DECLARE @MoneyUnit AS VARCHAR (32) = NULL;
DECLARE @Prefix AS VARCHAR (32) = NULL;
DECLARE @External_CallID AS VARCHAR (255) = NULL;
-- END OF CALL TABLE COLUMNS:
--------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------
-- SETTINGS
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION
PRINT ' ';
PRINT '===================================================================================';
EXEC [PrintSystemTime] 'Starting: [Call_Process_FromXoomTalk_UsingCursor]';
PRINT '===================================================================================';
--
-- It is assumed that the file loaded into the CallData table
-- only contains calls made in the year and month provided to this procedure
--
--
-- Move rows from the staging table [CallDATA] into the main table
-- [Call]. Some simple looks and modifications are made
--
-----------------------------------------------------------------
-- Define the cursor
-- WARNING - For simplicity keep column order in step with the
-- variable declarations, select and insert use!
--
DECLARE curNewData CURSOR FAST_FORWARD
FOR
SELECT Table1.*
FROM
( SELECT
-- ID is identity field
[id] AS [ExternalID]
, @PlatformId AS [PlatformID]
, ( SELECT SubQry.[ID]
FROM [dbo].[Actor] AS SubQry
WHERE SubQry.[ExternalID] = [NewData].client_reseller_id
) AS Actor_ResellerID
, ( SELECT SubQry.[ID]
FROM [dbo].[Actor] AS SubQry
WHERE SubQry.[ExternalID] = NewData.client_client_id
) AS Actor_ClientID
, ( SELECT SubQry.[ID]
FROM [dbo].[Actor] AS SubQry
WHERE SubQry.[ExternalID] = NewData.client_extension_id
) AS Actor_ExtensionID
-- This code prevent invalid values being loaded
-- that would be rejected byt eh foreigh key
, CASE [calltype]
WHEN 'out' THEN 'out'
WHEN 'in' THEN 'in'
WHEN 'local' THEN 'local'
WHEN 'elocal' THEN 'elocal'
ELSE NULL
END AS [calltype]
-----------------------------------------------------------------------------------------------
--
-- Initiated date/time is split to produce derrived columns
--
-- , [initiated] as [initiated] -- Useful for testing
-- NOTE that CAST will convert 0000-00-00 00:00:00 to be NULL
, TRY_CAST([initiated] AS DATE) AS [CallInitiatedDate]
, TRY_CAST([initiated] AS TIME(0)) AS [CallInitiatedTimeHH24MI]
, ( SELECT isnull(GroupWithTHISID,[ID]) as ID
FROM [dbo].[DimTimePeriod] AS SubQry
WHERE SubQry.[ActorID] = NewData.[client_client_id]
AND TRY_CAST([initiated] AS TIME(0)) >= SubQry.[StartTimeHH24MI]
AND TRY_CAST([initiated] AS TIME(0)) < SubQry.[EndTimeHH24MI]
) AS [TimePeriodID]
-- What format are their dates! Do they have nano secs
, TRY_CAST([answer] as datetime2) AS [CallAnswered]
, iif([answer] is null,0,1) AS [CallAnsweredYN]
-----------------------------------------------------------------------------------------------
, ( SELECT X.[ID]
FROM [dbo].[DimCallDisposition] AS X
WHERE X.[Disposition] = NewData.[disposion]
) AS [CallDispositionID]
-----------------------------------------------------------------------------------------------
--
-- Population of Country and Area require significant processing that is done later
--
, NULL AS [CountryID]
, NULL AS [AreaID]
-----------------------------------------------------------------------------------------------
-- Useful for testing
-- , [extension_number] AS [FromClientAndNumber]
, RIGHT([extension_number],LEN([extension_number])-CHARINDEX('*',[extension_number],1)) AS [FromNumber]
, CASE LEFT([partyid],2)
WHEN '00' THEN
-- Replace 00 with a + ie for all international numbers
'+' + SUBSTRING([partyid],3,9999)
ELSE [partyid]
END AS [ToNumber]
, [duration] AS [CallDuration_SECS]
-----------------------------------------------------------------------------------------------
-- COST columns
--
-- NOTE: Some data in the cost fields in the file from the xoomtalk platform
-- was in scientific notation format
-- eg 4.2e-05 4.2e-05 7.2e-05 6.2e-05 8.3e-05 8.3e-05
-- Note that these were all very small values!
, CASE
WHEN [costext] like '%E-%' THEN LTRIM(RTRIM(TRY_CAST(TRY_CAST([costext] AS FLOAT) AS DECIMAL(10,6))))
WHEN [costext] like '%E+%' THEN NULL
ELSE TRY_CAST([costext] AS DECIMAL(10,6))
END AS [CallCostToExtension]
, CASE
WHEN NewData.[costcl] like '%E-%' THEN LTRIM(RTRIM(TRY_CAST(TRY_CAST([costcl] AS FLOAT) AS DECIMAL(10,6))))
WHEN [costcl] like '%E+%' THEN NULL
ELSE TRY_CAST([costcl] AS DECIMAL(10,6))
END AS [CallCostToClient]
, CASE
WHEN NewData.[costres] like '%E-%' THEN LTRIM(RTRIM(TRY_CAST(TRY_CAST([costres] AS FLOAT) AS DECIMAL(10,6))))
WHEN [costres] like '%E+%' THEN NULL
ELSE TRY_CAST([costres] AS DECIMAL(10,6))
END AS [CallCostToReseller]
, CASE
WHEN NewData.[costadmin] like '%E-%' THEN LTRIM(RTRIM(TRY_CAST(TRY_CAST([costadmin] AS FLOAT) AS DECIMAL(10,6))))
WHEN [costadmin] like '%E+%' THEN NULL
ELSE TRY_CAST([costadmin] AS DECIMAL(10,6))
END AS [CallCostToAdmin]
-----------------------------------------------------------------------------------------------
, [flow] AS Flow
, [start] AS [CallStart]
, [moneyunit] AS [moneyunit]
, [prefix] AS [prefix]
, [callid] AS [External_CallID]
-----------------------------------------------------------------------------------------------
FROM [dbo].[CallDATA] AS NewData
WHERE ( (@LimitImportToRowsWithThisCallType IS NULL)
OR NewData.[calltype] = @LimitImportToRowsWithThisCallType
)
AND ( (@LimitImportToRowsWithThisFlow IS NULL)
OR NewData.[flow] = @LimitImportToRowsWithThisFlow
)
AND TRY_CAST([initiated] as datetime2) >= @StartOfMonthDate
AND TRY_CAST([initiated] as datetime2) <= @EndOfMonthDate
) as Table1
ORDER BY [calltype]
, [flow]
, [CallInitiatedDate]
, [CallInitiatedTimeHH24MI]
, [CallDuration_SECS]
;
-----------------------------------------------------------------
-- Open and use the cursor
--
PRINT '------------------------------------------------------------------';
PRINT 'ABOUT TO LIST the external_ID of any ROWS that failed to insert';
OPEN curNewData;
SET @FETCHSTATUS = 0;
SET @CountOfRowFailures = 0;
SET @CountOfRowSuccesses = 0;
WHILE @FETCHSTATUS = 0
BEGIN
FETCH NEXT FROM curNewData
INTO
@External_ID
, @PlatformID2
, @ActorIDOfReseller
, @ActorIDOfClient
, @ActorIDOfExtension
, @CallType
, @CallInitiatedDate
, @CallInitiatedTimeHH24MI
, @TimePeriodID
, @CallAnswered
, @CallAnsweredYN
, @CallDispositionID
, @CountryID
, @CallPrefixID
, @FromNumber
, @ToNumber
, @CallDuration
, @CallCostToExtension
, @CallCostToClient
, @CallCostToReseller
, @CallCostToAdmin
, @Flow
, @CallStart
, @MoneyUnit
, @Prefix
, @External_CallID
;
BEGIN TRY
INSERT INTO dbo.Call
(
[External_ID]
, [PlatformID]
, [ActorID-OfReseller]
, [ActorID-OfClient]
, [ActorID-OfExtension]
, [CallType]
, [CallInitiatedDate]
, [CallInitiatedTimeHH24MI]
, [TimePeriodID]
, [CallAnswered]
, [CallAnsweredYN]
, [CallDispositionID]
, [CountryID]
, CallPrefixID
, [FromNumber]
, [ToNumber]
, [CallDuration]
, [CallCostToExtension]
, [CallCostToClient]
, [CallCostToReseller]
, [CallCostToAdmin]
, [Flow]
, [CallStart]
, [MoneyUnit]
, [Prefix]
, [External_CallID]
)
VALUES
( @External_ID
, @PlatformID2
, @ActorIDOfReseller
, @ActorIDOfClient
, @ActorIDOfExtension
, @CallType
, @CallInitiatedDate
, @CallInitiatedTimeHH24MI
, @TimePeriodID
, @CallAnswered
, @CallAnsweredYN
, @CallDispositionID
, @CountryID
, @CallPrefixID
, @FromNumber
, @ToNumber
, @CallDuration
, @CallCostToExtension
, @CallCostToClient
, @CallCostToReseller
, @CallCostToAdmin
, @Flow
, @CallStart
, @MoneyUnit
, @Prefix
, @External_CallID
)
;
SET @CountOfRowSuccesses = @CountOfRowSuccesses + 1;
END TRY
BEGIN CATCH
-- Ignore error
PRINT CONCAT(@External_ID, '|', ERROR_NUMBER(), '|', ERROR_MESSAGE());
SET @CountOfRowFailures = @CountOfRowFailures + 1;
END CATCH;
SET @FETCHSTATUS = @@FETCH_STATUS;
END;
CLOSE curNewData;
DEALLOCATE curNewData;
PRINT '------------------------------------------------------------------';
PRINT CONCAT('Count Of Row Failures =', @CountOfRowFailures)
PRINT CONCAT('Count Of Row Successes=', @CountOfRowSuccesses)
PRINT '------------------------------------------------------------------';
COMMIT;
PRINT '################################ ';
PRINT 'COMMIT DONE';
PRINT '################################ ';
EXEC [PrintSystemTime] 'Ending: [Call_Process_FromXoomTalk_UsingCursor]';
PRINT '=========================================================================';
END TRY
BEGIN CATCH
IF @Raise1Print2Both3 = 2 OR @Raise1Print2Both3 = 3
EXEC [dbo].[ErrPrintErrorDetails]
@CalledFromProcedure= '[dbo].[Call_Process_FromXoomTalk_UsingCursor]'
, @ExplainitoryErrorTextForUser = NULL
, @AddDottedLine = 1;
EXEC [PrintSystemTime] 'Ending with Error: [Call_Process_FromXoomTalk_UsingCursor]' ;
PRINT '=========================================================================';
ROLLBACK;
PRINT '################################ ';
PRINT 'ROLLBACK DONE';
PRINT '################################ ';
IF @Raise1Print2Both3 = 1 OR @Raise1Print2Both3 = 3
THROW;
END CATCH;
END
答案 0 :(得分:1)
不幸的是,当您从Oracle迁移到SQL Server时,有时需要更改策略。
回到你的情况,如果你正在处理一个大数据集而你想捕获坏行,请尝试给你的原始导入表一个唯一的id列,从最小的id开始,将有限的行复制到新表(每次都使用cast,而不是try_cast)。当您看到错误时,插入将失败,您可以减少插入行的数量,直到到达坏行。将它移到&#34;坏行&#34;并继续尝试其他数据。复制完所有行后,您有一个用于良好行的表和一个用于坏行的表。然后处理坏的。
这可能不是快速解决方案,但您不必花费太多时间来完善您的脚本,而且基本上它是一次性解决方案。
这是概念:
Alter table imported_table add lid int identity(1,1) not null primary key;
select top 1 into bad_rows from imported_table ;
truncate table bad_rows;
然后运行此代码:
declare @last_id int, @r int,@c int;
select @last_id= 0; @r = 100;
while @last_id >=0 begin
begin try
insert into new_table (.....)
select top(@r) .... from imported_table where lid>@last_id order by lid;
select @c=@@rowcount;
if @c<1 select @last_id=-1 --- end of loop
else select @last_id=@last_id + @c;
select @r = 100;
end try
begin catch
if @r <2 begin
insert into bad_rows(...)
select top(@r)... from imported_table
where lid>@last_id order by lid;
select @lid=@lid+1;
end
else select @r=@r / 2;
end catch;
end;
答案 1 :(得分:0)
其他人已经表示使用临时表是可能的。
如果要轻松创建与源具有相同数据类型的临时表,可以使用Select INTO语法,例如
Select *
INTO #tmp
From mytable
Where 1 = 0
您可以仅使用选择列,联接等来创建临时工作空间,而无需声明变量和数据类型