【问题标题】:Tricky SQL including outer join and case棘手的 SQL,包括外连接和大小写
【发布时间】:2011-07-28 04:14:56
【问题描述】:

我使用来自http://geonames.org 的数据。表结构如下:

GN_Name 1 - 0:N GN_AlternateName

它们链接在:

(PK)GN_Name.GeoNameId == (FK)GN_AlternateName.GeoNameId

GN_Name 是包含所有地名的主表。 GN_AlternateName 包含其他语言的名称(如果有)。

前:

GN_Name.Name                   - Stockholm

GN_AlternateName.AlternateName - Estocolmo (if IsoLanguage=="es")

规则:

  • 如果指定语言存在 GN_AlternateName.AlternateName 并且它以搜索字符串开头,我想使用它。

  • 如果不是,我想使用 GN_Name.Name 如果它以搜索字符串开头。

  • 我希望 GeoNameId 是唯一的。

基本上我只能在第一条记录中加入外部连接,但这似乎会降低性能。

我有以下 SQL(从 LINQ 查询基本修改的 SQL)。问题是,如果搜索字符串以“stock”开头,它只会找到“Estocolmo”。 "estoc" 什么也没产生。

select 
distinct(n.GeoNameId) as Id,
an.IsoLanguage,
CASE WHEN (an.AlternateName like N'estoc%') 
    THEN an.AlternateName
    ELSE n.Name 
END AS [The name we are going to use]
from GN_Name as n
LEFT OUTER JOIN GN_AlternateName as an
ON n.GeoNameId = an.GeoNameId
AND 'es' = an.IsoLanguage
WHERE n.Name like N'estoc%'

更新

感谢 Rahul 和 Lee D。

我现在有以下内容:

select 
distinct(n.GeoNameId) as Id,
an.IsoLanguage,
CASE WHEN (an.AlternateName like N'estoc%') 
    THEN an.AlternateName
    ELSE n.Name 
END AS [The final name]
from GN_Name as n
LEFT OUTER JOIN GN_AlternateName as an
ON n.GeoNameId = an.GeoNameId
AND 'es' = an.IsoLanguage
WHERE (n.Name LIKE N'estoc%' OR an.AlternateName LIKE N'estoc%')

这会对an.AlternateName 执行两次LIKE。有什么办法可以摆脱 LIKE 子句?

更新 2 Andriy M 使用 COALESCE 做了一个不错的替代查询。我稍微改了一下,结果如下:

SELECT Id, LocalisedName
FROM (
  SELECT
    n.GeoNameId AS Id,
    an.IsoLanguage,
    COALESCE(an.AlternateName, n.Name) AS LocalisedName
  FROM n
    LEFT JOIN GN_AlternateName AS an ON n.GeoNameId = an.GeoNameId
    AND IsoLanguage = 'es'
) x
WHERE LocalisedName LIKE 'estoc%'

这个查询完全符合我的要求。谢谢!

【问题讨论】:

  • 首先,您似乎错误地认为DISTINCT 仅适用于n.GeoNameId,而事实并非如此。 DISTINCT 适用于所有正在检索的列和表达式。
  • 另外,如果您要搜索的本地化名称不存在怎么办?也就是说,结果实际上是可以预测的:你不会得到任何行。但这可以接受吗?
  • @Andriy M 你是对的。当本地化名称不存在时,我确实不需要标准名称。但是,我只想要每个位置的一条记录。有什么好的方法可以实现吗?

标签: sql-server-2008 case outer-join


【解决方案1】:

如果我理解正确,在您的示例中,值“Estocolmo”位于 GN_AlternateName.AlternateName 列中,因此将被仅查看 GN_Name.Name 的 where 子句过滤掉。如果把最后一行 SQL 改成:

WHERE n.Name LIKE N'estoc%' OR an.AlternateName LIKE N'estoc%'

我假设 'estoc%' 是您的搜索字符串。

【讨论】:

    【解决方案2】:

    我猜你需要修改 WHERE 子句来检查GN_AlternateName

    WHERE n.Name like N'estoc%' OR an.AlternateName like 'N'estoc%'
    

    【讨论】:

      【解决方案3】:

      这是一个可能的问题解决方案,它使用了稍微不同的方法:

      SELECT Id, LocalisedName
      FROM (
        SELECT
          n.GeoNameId AS Id,
          an.IsoLanguage,
          COALESCE(an.AlternateName, n.Name) AS LocalisedName
        FROM GN_Name AS n
          LEFT JOIN GN_AlternateName AS an ON n.GeoNameId = an.GeoNameId
            AND IsoLanguage = 'es'
      ) x
      WHERE LocalisedName LIKE 'estoc%'
      

      (根据您的更新进行了更改。)

      【讨论】:

      • 合并。好的。我每天都在学习新东西。我稍微更改了查询,使其返回本地化名称不可用的行。将 WHERE IsoLanguage = 'es' 作为最后的条件以加入 GN_AlternateName 为前提。我已经用最终查询更新了问题。
      • 如果您同意我之前的观点,请更新您的查询,我会给您信任。如果我错了,请告诉我。
      • 感谢您的更新。搜索“斯德哥尔摩”时,您的选择将返回“Estocolmo”,这不是所需的结果。我不希望本地化名称在搜索时优先于标准名称。它应该根据用户输入的字符返回建议,而不是某种翻译。
      • 嗯,所以搜索“stock%”而不是“estoc%”不是一个选项,因为用户输入应该按原样传递给查询。好的。但是如果没有与用户输入匹配的建议,即“estoc%”,应该返回什么?
      • 如果 GN_Name.Name 或 GN_AlternateName.AlternateName 中没有匹配项,则不应返回任何内容。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-28
      • 1970-01-01
      • 1970-01-01
      • 2021-02-07
      • 2018-10-25
      • 1970-01-01
      相关资源
      最近更新 更多