【问题标题】:sql server cursorsql server 游标
【发布时间】:2011-05-03 02:59:58
【问题描述】:

我想将数据从一个表(原始数据,所有列都是 VARCHAR)复制到另一个表(用相应的列格式格式化)。

为了将rawdata 表中的数据复制到formatted 表中,我使用游标来识别受影响的行。我需要在错误日志表中记录该特定行,跳过它,然后继续复制剩余的行。

复制需要更多时间。有没有其他方法可以实现这一目标? 这是我的查询

DECLARE @EntityId Varchar(16) ,
        @PerfId Varchar(16), 
        @BaseId Varchar(16) ,
        @UpdateStatus Varchar(16) 

DECLARE CursorSample CURSOR FOR 
     SELECT EntityId, PerfId, BaseId, @UpdateStatus
       FROM RawdataTable 
     --Returns 204,000 rows

OPEN CursorSample 
FETCH NEXT FROM CursorSample INTO @EntityId,@PerfId,@BaseId,@UpdateStatus

  WHILE @@FETCH_STATUS = 0 
  BEGIN 
    BEGIN TRY
    --try insertting row in formatted table

    Insert into FormattedTable
      (EntityId,PerfId,BaseId,UpdateStatus)
    Values
      (Convert(int,@EntityId),
       Convert(int,@PerfId),
       Convert(int,@BaseId),
       Convert(int,@UpdateStatus)) 
    END TRY
  BEGIN CATCH
  --capture Error EntityId in errorlog table

   Insert into ERROR_LOG
     (TableError_Message,Error_Procedure,Error_Log_Time)
   Values 
     (Error_Message()+@EntityId,’xxx’, GETDATE())

END CATCH

FETCH NEXT FROM outerCursor INTO @EntityId, @BaseId 
END 

CLOSE CursorSample 
DEALLOCATE CursorSampler –cleanup CursorSample

【问题讨论】:

  • 你遇到过这个问题吗?

标签: sql sql-server tsql cursor


【解决方案1】:

您应该能够使用 INSERT INTO 语句将记录直接放入格式化表中。 INSERT INTO 将比使用游标执行得更好。

INSERT INTO FormattedTable
SELECT
    CONVERT(int, EntityId),
    CONVERT(int, PerfId),
    CONVERT(int, BaseId),
    CONVERT(int, UpdateStatus)
FROM RawdataTable
WHERE
    IsNumeric(EntityId) = 1
    AND IsNumeric(PerfId) = 1
    AND IsNumeric(BaseId) = 1
    AND IsNumeric(UpdateStatus) = 1

请注意,IsNumeric 有时会为值 that will then fail on CONVERT 返回 1。例如,IsNumeric('$e0') 将返回 1,因此您可能需要创建一个更强大的用户定义函数来确定字符串是否为数字,具体取决于您的数据。

另外,如果您需要所有无法移动到格式化表中的记录的日志,只需修改 WHERE 子句:

INSERT INTO ErrorLog
SELECT
    EntityId,
    PerfId,
    BaseId,
    UpdateStatus
FROM RawdataTable
WHERE
    NOT (IsNumeric(EntityId) = 1
    AND IsNumeric(PerfId) = 1
    AND IsNumeric(BaseId) = 1
    AND IsNumeric(UpdateStatus) = 1)

编辑
与其直接使用 IsNumeric,不如创建一个自定义 UDF 来告诉您是否可以将字符串转换为 int。这个功能对我有用(尽管测试有限):

CREATE FUNCTION IsInt(@value VARCHAR(50))
RETURNS bit
AS
BEGIN
    DECLARE @number AS INT
    DECLARE @numeric AS NUMERIC(18,2)
    SET @number = 0
    IF IsNumeric(@value) = 1
      BEGIN
        SET @numeric = CONVERT(NUMERIC(18,2), @value)
        IF @numeric BETWEEN -2147483648 AND 2147483647
            SET @number = CONVERT(INT, @numeric)
      END

    RETURN @number
END
GO

用于插入格式化表的更新后的 SQL 将如下所示:

INSERT INTO FormattedTable
SELECT
    CONVERT(int, CONVERT(NUMERIC(18,2), EntityId)),
    CONVERT(int, CONVERT(NUMERIC(18,2), PerfId)),
    CONVERT(int, CONVERT(NUMERIC(18,2), BaseId)),
    CONVERT(int, CONVERT(NUMERIC(18,2), UpdateStatus))
FROM RawdataTable
WHERE
    dbo.IsInt(EntityId) = 1
    AND dbo.IsInt(PerfId) = 1
    AND dbo.IsInt(BaseId) = 1
    AND dbo.IsInt(UpdateStatus) = 1

处理 NULL 可能有点奇怪(如果传入 NULL,我的函数将返回 0,即使 INT 肯定可以为 null),但这可以根据 NULL 值应该发生的情况进行调整RawdataTable

【讨论】:

  • IsNumeric 不会返回误报。它只是回答了一个与大多数人想的不同的问题——它回答了“这个字符串可以转换为 int 吗?”的问题。 - 它回答了“可以将此字符串转换为其中一种数字类型吗?”的问题。不幸的是,即使答案是肯定的,也没有一个函数“可以将这个字符串转换为类型 X 吗?” (这很有用),或者“这个字符串可以转换成哪些数字类型?” (这可能很有用)
  • @Damien_The_Unbeliever 感谢您的反馈。我只是想指出 IsNumeric 有时会返回 1 来表示在 CONVERT 上会失败的值,而且我的措辞很糟糕。我更新了答案来说明这一点。
  • 对于 Int,我们可以选择 IsInt,对于 date、money、float、decimal 数据类型。
  • @bala 我认为您必须编写与 IsInt 类似的函数来验证其他类型。
【解决方案2】:

您可以在游标定义中放置WHERE 子句,以便首先选择有效记录。您可能需要创建一个函数来确定有效性,但它应该比遍历它们更快。

实际上,您可能希望创建一个包含 invalid 记录的临时表,以便记录错误,然后仅在不在临时表中的行上定义游标。

【讨论】:

    【解决方案3】:

    插入会比光标好得多。 由于光标仅在您的 PC 内存中工作并减慢 SQL Server 的优化。我们应该避免使用游标,但(当然)在某些情况下无法避免使用游标。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-01
      相关资源
      最近更新 更多