MS SQL CONCAT 与“+”查询的区别(使用 Doctrine 2)

Posted

技术标签:

【中文标题】MS SQL CONCAT 与“+”查询的区别(使用 Doctrine 2)【英文标题】:MS SQL CONCAT vs "+" query difference (with Doctrine 2) 【发布时间】:2019-12-06 11:05:08 【问题描述】:

我正在使用 Doctrine 2 接口访问 MS SQL 数据库。我发现了双方都非常有趣的问题:Doctrine 2 和 MS SQL。

Doctrine 2 出于某种原因更改查询

$this->getEntityManager()->createQueryBuilder()
            ->from(table::class, 'il')
            ->select('CONCAT(il.col1,il.col2,il.col3) AS concated')

更改为类似(它使用自己的别名):

SELECT (t0_.col1+ t0_.col2+ t0_.col3) AS sclr_0 FROM tableName t0_

最重要的 - 它将CONCAT 更改为带有“+”的表达式。但它工作正常(或者看起来确实如此),直到我想将连接字符串与其他表中的值进行比较......

$targetQueryBuilder->getEntityManager()->createQueryBuilder()
            ->from($namespace, 's')
            ->select('1')
            ->where('CONCAT(il1.col1, il1.col2, il1.col3) = s.controlValue')
            ->andWhere('s.tableName = :tablename')
            ->andWhere('s.tableRowIdentifier='.$rowIdentifier);

(我是凭记忆写这个查询的,如果有问题 - 没关系 - 是为了可视化问题) 使用 CONCAT 函数在 SQL Studio 中执行原始查询绝对可以正常工作。使用“+”则不然。所有具有 nvarchar 类型的连接列。问题是:区别在哪里?

我在原则中检查了大量组合以跳过将 CONCAT 映射到“+”但没有成功,我不得不编写一些虚拟词法分析器扩展来强制将 CONCAT 函数应用于最终查询。

其他(可能是特定于 ms sql server)的问题是: 测试的时候

SELECT
(i1_.NameUnternehmen + i1_.Land + i1_.Ort + i1_.Straße + i1_.Postleitzahl) AS a,
CONCAT(i1_.NameUnternehmen , i1_.Land , i1_.Ort , i1_.Straße , i1_.Postleitzahl) AS c
FROM tablename

Straße 列名称在此问题中看起来并不重要) 使用 WHERE 语句比较连接字符串与控制值时,选择结果不同。

->where('CONCAT(il1.col1, il1.col2, il1.col3) = s.controlValue') - 一切都正确

->where('(il1.col1 + il1.col2 + il1.col3) = s.controlValue') - 结果列“a”为空。我刚刚测试过,在其他表上它有时不为空。我不知道为什么。 “c”列总是正确的。

【问题讨论】:

Larnu 很好地解释了这些差异。你可以做 ISNULL(il1.col1, '') + ...COALESCE(il1.col1, '') + ... 但是 eww。不知道为什么教义会改变查询。看起来像一个大错误。 【参考方案1】:

CONCAT+ 操作不同。如果任何表达式在使用+ 时具有值NULL,则将返回NULL。对于CONCAT,它会将NULL 隐式替换为'',因此不会返回NULL

CONCAT 还会将表达式中的任何值隐式转换为(n)varchar,其中+ 使用Data Type Precedence 来确定数据类型并将表达式中的其他值隐式转换为最高优先级的数据类型.

例子:

SELECT c1,
       c2,
       c3,
       c4,
       c1 + c2 + c3 + c4, --NULL 
       CONCAT(c1, c2, c3, c4) --'abcdefxyc'
FROM (VALUES('abc','def',NULL,'xyz'))V(c1, c2, c3, c4);

隐式转换:

SELECT c1 + c2 + c3 + c4
FROM (VALUES('abc','def',7,'xyz'))V(c1, c2, c3, c4);

消息 245,第 16 级,状态 1,第 9 行 将 varchar 值 'abcdef' 转换为数据类型 int 时转换失败。

SELECT CONCAT(c1, c2, c3, c4) --'abcdef7xyz'
FROM (VALUES('abc','def',7,'xyz'))V(c1, c2, c3, c4);

有趣但预期的行为:

SELECT c1 + c2 + c3 + c4 --NULL
FROM (VALUES('abc','def',NULL,7))V(c1, c2, c3, c4);

SELECT c1 + c2 + c3 + c4 --123456790
FROM (VALUES('123','456','789',1))V(c1, c2, c3, c4);

这种行为的原因是因为表达式是从左到右计算的。首先,您有'abc' + 'def' = 'abcdef',然后是'abcdef' + NULL = NULL (varchar)。最后你有了NULL + 7 = NULL,这很好,因为NULL可以隐式转换为int

对于后者,你有'123' + '456' = '123456',然后是'123456' + '789' = '123456789'。最后你有'123456789' + 1 = 123456789 + 1 = 123456790。请注意,在最后一个表达式中,varchar 首先被隐式转换为int,然后+ 运算符充当加法,而不是串联。

【讨论】:

谢谢!我从没想过将 CONCAT 更改为“+”的选项,但 php Doctrine 2 将所有“CONCAT”更改为“+”。我浪费了很多时间来寻找可以禁用此替换的解决方案:(【参考方案2】:

CONCAT 和 '+' 都不同。 在 mysql concat 中简单地组合/加入我们的数据/字符串/数字。 主要区别在于数据类型。

【讨论】:

以上是关于MS SQL CONCAT 与“+”查询的区别(使用 Doctrine 2)的主要内容,如果未能解决你的问题,请参考以下文章

oracle的sql查询结果拼接

如何使 MS Access 直通查询在 SQL Server 中正确运行

MS SQL:在 SELECT 中使用函数调用优化查询

Laravel 8 - MS SQL - 查询生成器 - 使用 DB Raw。尝试使代码正确,使其像工作的 MSSQL 代码一样工作

SQL GROUP_CONCAT + SUM + AVG

SQL关联查询 直接join 和子查询的区别