【问题标题】:Convert Date Stored as VARCHAR into INT to compare to Date Stored as INT将存储为 VARCHAR 的日期转换为 INT 以与存储为 INT 的日期进行比较
【发布时间】:2015-10-10 00:38:26
【问题描述】:

我使用的是 SQL Server 2014。我认为我的请求相当简单。我有一个表包含一个包含存储为VARCHAR 的日期值的字段,另一个表包含一个包含存储为INT 的日期值的字段。

VARCHAR 字段中的日期值存储如下:2015M01
INT字段中的数据值是这样存储的:201501

我需要使用EXCEPT 将这些表相互比较。我的思考过程是以某种方式从VARCHAR 值中提取或修剪“M”,看看它是否能让我比较两者。如果有人有更好的想法,例如使用 CAST 更改日期格式或其他建议,请随时提出。

我还担心即使从VARCHAR 中提取“M”也可能会阻止比较,因为一个仍然是VARCHAR,另一个是INT。如果可能的话,通过 T-SQL 查询进行即时转换,这也是很好的建议。 :)

【问题讨论】:

    标签: sql-server tsql type-conversion sql-server-2014


    【解决方案1】:

    REPLACE 字符串,然后 CONVERT 为整数

    SELECT A.*, B.*
    FROM TableA A 
    INNER JOIN 
       (SELECT intField 
        FROM TableB
       ) as  B
    ON CONVERT(INT, REPLACE(A.varcharField, 'M', ''))  = B.intField
    

    【讨论】:

    • 有趣。尽管我目前在两个查询之间使用 except ,但这看起来可能有效。所以我有一个包含多个连接的查询,然后是一个介于两者之间的查询。所以我不确定我可以按照你的建议使用加入吗?也许别名出每个查询?我基本上是在比较所有数据,并希望什么都不返回,这意味着所有数据都匹配。
    • 是的,只需将您的查询放在() 之间并使用别名或使用一些 CTE 以使内容更具可读性
    【解决方案2】:

    既然您说您已经有了查询并且正在使用EXCEPT,您可以简单地更改查询中包含VARCHAR 值的那个“日期”字段的定义,使其匹配INT 的格式另一个查询。例如:

    SELECT Field1, CONVERT(INT, REPLACE(VarcharDateField, 'M', '')) AS [DateField], Field3
    FROM   TableA
    EXCEPT
    SELECT Field1, IntDateField, Field3
    FROM   TableB
    

    HOWEVER,虽然我意识到这可能不可行,但如果你能做到这一点,你最好的选择是改变带有VARCHAR字段的表中数据的存储方式,使其实际上是一个INT 与表格的格式相同,其中数据已存储为INT。这样你就不用担心这种情况了。

    意思:

    • INT 字段添加到具有VARCHAR 字段的表中。
    • 对该表执行UPDATE,将INT 字段设置为删除M 的字符串值。
    • 更新外部服务(应用程序、ETL 等)使用的任何 INSERT 和/或 UPDATE 存储过程,以执行相同的 M 删除逻辑。然后您不必更改任何应用程序代码插入和更新。你甚至不需要告诉任何人你做了这件事。
    • 更新外部服务(应用程序、ETL 等)使用的任何“get”/SELECT 存储过程以执行相反的逻辑:将INT 转换为VARCHAR,并在退出时添加M。然后,您不必更改任何从数据库获取数据的应用程序代码。你甚至不需要告诉任何人你做了这件事。

    这是为您的数据库提供存储过程 API 非常方便的众多原因之一。我想一个 ORM 可以重建,但你仍然需要重新编译,即使所有的代码引用都自动更新了。但是在“幕后”进行数据类型更改(甚至将字段移动到不同的表,或者甚至用简单的CASE 语句替换字段)并屏蔽它,以便您控制之外的任何代码都不知道发生了变化,并不像大多数人想象的那么困难。我已经完成了所有这些操作(数据类型更改、将字段移动到不同的表、用简单的逻辑替换字段等),这会为您赢得很多时间,直到可以更新应用程序代码。那可能是另一个处理这个问题的团队。也许他们的日程安排不允许在 3 个月内对该领域(加上测试)进行任何更改。好的。当他们准备好时,它会在那里等他们。如果有多个区域需要更新,则可以一次完成一个。您甚至可以为任何更新的应用程序代码创建新的存储过程以并行运行,以将正确的INT 数据类型作为输入参数。一旦所有对 VARCHAR 值的引用都消失了,然后删除这些存储过程的原始版本。

    【讨论】:

    • 这似乎是做我想做的最好的方式。干杯!是的,我会向我的团队询问为什么我们将该值存储为 VARCHAR 而非 INT。
    【解决方案3】:

    如果您想要第一个表中的所有内容而不是第二个表中的所有内容,您可以考虑这样的事情:

    select t1.*
    from t1
    where not exists (select 1
                      from t2
                      where cast(replace(t1.varcharfield, 'M', '') as int) = t2.intfield
                     );
    

    对于您的目的,这应该与except 足够接近。

    我应该补充一点,您可能需要在 where 语句中包含其他列。但是,问题只提到了一列,所以我不知道那些是什么。

    【讨论】:

    • 这与胡安·卡洛斯·奥罗佩萨 (Juan Carlos Oropeza) 发布的第一个答案有何实质性不同?
    • @MaxVernon 。 . . JOINEXCEPT 的错误解决方案。如果该问题使用了LEFT JOIN,那么它会更合适(尽管需要对查询进行更多修改)。
    • @MaxVernon 和 Gordon:虽然我同意 Gordon 的观点,即使用 INNER JOIN(如 Juan 的回答所示)在这里没有任何意义,但我很好奇是否有特别的理由建议从 EXCEPT 移动到 WHERE NOT EXISTS 构造。这样做有什么好处吗?
    • @srutzky 。 . .我更喜欢NOT EXISTS,因为所有数据库都支持它。它也不需要仔细排列列和类型。
    【解决方案4】:

    您可以使用 char 列在表上创建一个持久视图,并在其中删除 M 的计算列。然后你可以JOIN 对包含INT 列的表的视图。

    CREATE VIEW dbo.PersistedView
    WITH SCHEMA_BINDING
    AS
    SELECT ConvertedDateCol = CONVERT(INT, REPLACE(VarcharCol, 'M', ''))
        --, other columns including the PK, etc
    FROM dbo.TablewithCharColumn;
    
    CREATE CLUSTERED INDEX IX_PersistedView
    ON dbo.PersistedView(<the PK column>);
    
    SELECT *
    FROM dbo.PersistedView pv
        INNER JOIN dbo.TableWithIntColumn ic ON pv.ConvertedDateCol = ic.IntDateCol;
    

    如果您提供两个表的实际详细信息,我将编辑我的答案以使其更清晰。

    与每次运行SELECT 语句时执行CONVERTREPLACE 相比,具有计算列的持久视图在连接两列的SELECT 语句上的性能要好得多。

    但是,持久化视图会稍微减慢插入基础表的速度,并且会阻止您对基础表进行 DDL 更改。

    如果您不希望通过模式绑定视图保留值,您可以在表本身上创建一个非持久化计算列,然后在该列上创建一个非聚集索引。如果您在WHEREJOIN 子句中使用计算列,您可能会看到一些好处。

    举例:

    CREATE TABLE dbo.PCT
    (
        PCT_ID INT NOT NULL
            CONSTRAINT PK_PCT
            PRIMARY KEY CLUSTERED
            IDENTITY(1,1)
        , SomeChar VARCHAR(50) NOT NULL
        , SomeCharToInt AS CONVERT(INT, REPLACE(SomeChar, 'M', ''))
    );
    
    CREATE INDEX IX_PCT_SomeCharToInt
    ON dbo.PCT(SomeCharToInt);
    
    INSERT INTO dbo.PCT(SomeChar)
    VALUES ('2015M08');
    
    SELECT SomeCharToInt
    FROM dbo.PCT;
    

    结果:

    【讨论】:

    • 好的开始,但是与其做一个完整的视图,不如创建一个非持久计算列作为ConvertedDateCol,然后在其上创建一个非聚集索引呢?然后,它实际上会被持久化并自然地与表聚集索引相关联,因此它将在此查询中被拾取。这行得通吗?
    • 好点,@srutzky(抱歉延迟回复!)我将更新我的答案以显示该选项。
    猜你喜欢
    • 2015-08-14
    • 2019-10-17
    • 1970-01-01
    • 1970-01-01
    • 2016-09-22
    • 1970-01-01
    • 2015-02-26
    • 2015-04-27
    • 1970-01-01
    相关资源
    最近更新 更多