将存储为 VARCHAR 的日期转换为 INT 以与存储为 INT 的日期进行比较

Convert Date Stored as VARCHAR into INT to compare to Date Stored as INT

我正在使用 SQL Server 2014。我认为我的要求很简单。我有一个 table 包含一个包含存储为 VARCHAR 的日期值的字段,另一个 table 包含一个包含存储为 INT 的日期值的字段。

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

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

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

REPLACE the string and then CONVERT 到整数

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

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

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;

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

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

但是,持久化视图会稍微减慢插入底层 table(s) 的速度,并且会阻止您对底层 tables 进行 DDL 更改。

如果您不希望通过模式绑定视图保留值,您可以在 table 本身上创建一个非持久计算列,然后在该列上创建一个非聚集索引.如果您在 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;

结果:

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

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

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

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

由于您说您已经有了查询并且正在使用 EXCEPT,您可以简单地更改包含 VARCHAR 值的查询中那个 "date" 字段的定义,以便它匹配其他查询的 INT 格式。例如:

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

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

含义:

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

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