SQL Server不支持相当于ORACLE的%TYPE和%ROWTYPE

时间:2015-05-12 20:43:00

标签: sql-server oracle tsql

我编写一个游标来从表中选择数据到变量。

使用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开发人员会想知道他们使用这个专业工具是多么幸运。

哈维

从下面的评论看来,有一种更简单的方法!非常感谢您的投入。我咆哮着,因为我有点开始......抱歉。

这里有更多背景信息:

  1. 我有一个csv文件,每列都有不可靠的数据。

  2. 我BULK将此csv文件加载到tableA中,其中所有列都声明为VARCHAR(100)。 (即使值的数据类型不是列中的预期,文件也会加载)

  3. 我使用单个语句将tableA中的数据传输到tableB中。 TableB与tableA具有相同的列,但tableB列具有各种数据类型 - 这是csv数据应具有的预期数据类型。

  4. (TableB还有一些其他未填写的文件列,例如IDENTITY列等。)。

    声明如下:

    INSERT INTO tableB (列表) 选择 ({select列的列表}) FROM tableB;

    上面的{列列表}中的每一列都包含TRY_CAST函数,以将其转换为与目标表相同的数据类型或NULL。

    1. 上面的步骤3工作正常,但我无法识别无效行。 所以我决定尝试使用游标并使用与步骤3中相同的select语句,但不包含每列的TRY_CAST。我在整个INSERT语句周围使用了一个TRY / CATCH,它在select语句的每一行上操作,以便在INSERT失败时记录无效行。

    2. 我现在写了第4步,但男孩,我希望我发布了一个"我该怎么做?"先问一下这里。我的代码看起来非常垃圾,我已将其发布在下面。

    3. 正如您可能已经说过的那样,我没有长时间使用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
      

2 个答案:

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

您可以仅使用选择列,联接等来创建临时工作空间,而无需声明变量和数据类型