【问题标题】:How to shorten long UNION ALL query in TSQL如何缩短 SQL 中的长 UNION ALL 查询
【发布时间】:2009-02-12 21:41:36
【问题描述】:

我需要缩短这个查询,虽然我很擅长 SQL,但我仍在学习。

    SELECT
        'doejoh',
        DATETIME,
        [Recipient-Address], [Message-Subject], [Sender-Address] 
    FROM
        dbo.Logs 
    WHERE
        LEFT([Recipient-Address], 6) IN ('doejoh') 
UNION ALL 
    SELECT
        'doejoh',
        DATETIME,
        [Recipient-Address], [Message-Subject], [Sender-Address] 
    FROM
        dbo.Logs 
    WHERE
        LEFT([Recipient-Address], 10) IN ('john.doe@g') 
UNION ALL 
    SELECT
        'doejoh',
        DATETIME,
        [Recipient-Address], [Message-Subject], [Sender-Address] 
    FROM
        dbo.Logs 
    WHERE
            LEFT([Sender-Address], 6) IN ('doejoh') 
    UNION ALL 
    SELECT
        'doejoh',
        DATETIME,
        [Recipient-Address], [Message-Subject], [Sender-Address] 
    FROM
        dbo.Logs 
    WHERE
            LEFT([Sender-Address], 10) IN ('john.doe@g')
    ORDER BY
        DateTime

我必须使用这个联合,因为在同一张表中,每个用户及其电子邮件地址有 4 种不同的可能性。话虽如此,我有 30 个用户,所以在整个查询中 30x4 将是 120 个组。第一列必须是用户名的原因是因为我在 Crystal Report 中使用该列。

我只是想为我的查询创建一些逻辑来缩短它,同时将每个用户“分配”到他们相应的第一列。

编辑添加

虽然这会缩短我的查询,但我仍然需要有 30 个联合:

SELECT
   'doejoh',
   DATETIME,
   [Recipient-Address], [Message-Subject], [Sender-Address] 
FROM
   dbo.Logs 
WHERE
   LEFT([Recipient-Address], 6) IN ('doejoh') OR
   LEFT([Recipient-Address], 10) IN ('john.doe@g') OR
   LEFT([Sender-Address], 6) IN ('doejoh') OR
   LEFT([Sender-Address], 10) IN ('john.doe@g')
ORDER BY
   DateTime

因为下一个用户将与前一个用户联合:

UNION ALL 
SELECT
   'doejan',
   DATETIME,
   [Recipient-Address], [Message-Subject], [Sender-Address] 
FROM
   dbo.Logs 
WHERE
   LEFT([Recipient-Address], 6) IN ('doejan') OR
   LEFT([Recipient-Address], 10) IN ('jane.doe@g') OR
   LEFT([Sender-Address], 6) IN ('doejan') OR
   LEFT([Sender-Address], 10) IN ('jan.doe@g')

等等等等……还有更短的方法吗?

【问题讨论】:

  • 如果我的判断正确,您希望返回 5 列的结果集,其中发件人地址或收件人地址的前 6 个为“doejoh”或“john.doe@g”和 10 个字符?
  • 是否可以使用 SenderID 或 RecipientID 或其他数字标识符来代替字符串模式匹配?
  • @Russ Cam - 它必须是字符串匹配。

标签: tsql logic


【解决方案1】:

您应该将查询重写为:

SELECT
   'doejoh',
   DATETIME,
   [Recipient-Address], [Message-Subject], [Sender-Address] 
FROM
   dbo.Logs 
WHERE
   LEFT([Recipient-Address], 6) IN ('doejoh') OR
   LEFT([Recipient-Address], 10) IN ('john.doe@g') OR
   LEFT([Sender-Address], 6) IN ('doejoh') OR
   LEFT([Sender-Address], 10) IN ('john.doe@g')
ORDER BY
   DateTime

在选择方面应该是一样的,我觉得只是更快更容易理解。

马克

【讨论】:

  • 我仍然有 30 个工会使用您上面的代码。我试图避免这种情况。
  • 为什么?您应该能够删除您的工会并用 WHERE 子句中的“OR”语句替换它们,恕我直言,这更具可读性
  • 另外,如果您需要检查两个用户,您可以将每个 WHERE 子句扩展为: LEFT([Recipient-Address], 6) IN ('doejoh', 'doejan')等等 --> 没有更多的 UNIONS :-)
  • 但是话又说回来,如果您要使用那么多用户 ID 进行搜索,最好创建一个单独的表来保存所有用户 ID,然后加入该搜索表(正如其他人所建议的)进一步向下)
  • @marc_s 因为对于我的所有用户,第一列将包含所有 john doe 的“doejoh”,所有 jane doe 的“doejan”等。我没有将所有位置分配给一个值'doejoh'..
【解决方案2】:

这样的方法不起作用有什么原因吗?

CREATE TABLE #TempNames
(
    shortname nvarchar(6),
    longname nvarchar(10)
)

INSERT INTO #TempNames (shortname, longname) VALUES('doejoh', 'john.doe@g')
INSERT INTO #TempNames (shortname, longname) VALUES('doejan', 'jan.doe@g')
INSERT INTO #TempNames (shortname, longname) VALUES('smibob', 'bob.smith@g')

SELECT
    #TempName.shortname,
    DATETIME,
    [Recipient-Address], [Message-Subject], [Sender-Address]
FROM
    dbo.Logs
INNER JOIN
    #TempNames
ON
    LEFT([Recipient-Address], 6) = #TempNames.shortname
OR
    LEFT([Recipient-Address], 10) = #TempNames.longname
OR
    LEFT([Sender-Address], 6) = #TempNames.shortname
OR
    LEFT([Sender-Address], 10) = #TempNames.longname

【讨论】:

  • 感谢 Robin Day 为我提供了一些有用的东西。我今天学到了一些新东西,对此我感激不尽...
【解决方案3】:

创建一个映射表并加入它。

例如。像

select user_name, DateTime .... 
from Logs
join Users on 
   LEFT([Recipient-Address], 6) IN (user_name) OR
   LEFT([Recipient-Address], 10) IN (user_email) OR
   LEFT([Sender-Address], 6) IN (user_name) OR
   LEFT([Sender-Address], 10) IN (user_email)

【讨论】:

    【解决方案4】:

    你不能只使用...

    SELECT
        'doejoh',
        DATETIME,
        [Recipient-Address], [Message-Subject], [Sender-Address] 
    FROM
        dbo.Logs 
     WHERE
        (LEFT([Recipient-Address], 10) IN ('john.doe@g'))
    or  (LEFT([Recipient-Address], 6) IN ('doejoh') )
    or  ( LEFT([Sender-Address], 10) IN ('john.doe@g'))
    or  (LEFT([Sender-Address], 6) IN ('doejoh') )
    

    【讨论】:

      【解决方案5】:

      用这 30 个人的电子邮件地址创建一个表。 表:电子邮件 列:short6、long10、电子邮件

      然后只使用 1 个联合全部

      Select Emails.short6, Logs.DateTime, Logs.[Recipient-Address], Logs.[Message-Subject], Logs.[Sender-Address]
      From Emails JOIN Log on Emails.email = Log.[Recipient-Address]
      Where LEFT([Recipient-Address], 6) = Emails.short6 
      or LEFT([Recipient-Address], 10) = Emails.long10
      
      union all
      
      Select Emails.short6, Logs.DateTime, Logs.[Recipient-Address], Logs.[Message-Subject], Logs.[Sender-Address]
      From Emails JOIN Log on Emails.email = Log.[Sender-Address]
      Where LEFT([Sender-Address], 6) = Emails.short6 
      or LEFT([Sender-Address], 10) = Emails.long10
      

      【讨论】:

        猜你喜欢
        • 2012-09-26
        • 1970-01-01
        • 1970-01-01
        • 2013-04-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多