【问题标题】:How to write an SQL query that uses wild-card map to do transformation如何编写使用通配符映射进行转换的 SQL 查询
【发布时间】:2015-05-12 19:13:44
【问题描述】:

我有一个包含许多列的源表 A。我想将源表转换为目标表。我想要一个映射表,其列与源表 A 相同,并且包含构成翻译的行。

这里是A表的一个例子:

COL1    COL2    COL3
aktie   ja         2
aktie   nej        3
obli    ja         2

这是映射表

COL1    COL2    COL3        TRANSFORM
aktie   ja      NULL            3
aktie   NULL    NULL            4

现在,我们的想法是将源表与映射连接起来,并返回转换后的值。 NULL 的使用应用作通配符。所以我想要的结果应该是表 A 中的第一行将匹配映射表中的第一行并返回值 3

对于第二行 - 这是我的挑战 - 我希望它与映射表中的第二行匹配,因为它与已经具有值的行不匹配(这将导致转换后的值 3)并且作为第二个映射行在第二列中为 NULL,应将其视为通配符(尽管也要考虑映射表中的其他行)。

我的第一次尝试是这样的

select A.*, m.res
from tab1 A
inner join mapping m on t.col1 = isnull(m.col1, t.col1) 
                     and t.col2 = isnull(m.col2, t.col2)
and ...

但问题是 isnull(..,..) 将匹配所有内容,并且不仅返回匹配项,但列出的可能值除外,这些值会导致不同的转换。

我正在寻找一种通用解决方案,它适用于具有任意数量列的任何表格,而不仅仅是这里提到的这个特定表格布局。

我一直在思考这个问题,但似乎无法真正想出解决方案,所以请帮忙:)

【问题讨论】:

  • 您使用的是哪个平台?在我看来,最好是 SQL Server、Oracle 或 DB2(不适用于 mysql)
  • 最好,它应该同时在 SQL server 或 Oracle 上工作,如果你的想法只在其中一个上工作,SQL Server 是首选
  • 我没有按照您认为您的想法看到的问题:“问题是 isnull(..,..) 将匹配所有内容 [...]。”如果m.col1 为空,isnull(m.col1, t.col1)(或coalesce(m.col1, t.col1))将返回t.col1,否则返回m.col1。这与将 null 视为通配符会产生什么不同的行为?
  • 这个想法是应该将其视为通配符,但如果没有其他匹配项,则仅应将其视为通配符。在示例中,如果源表具有 col1 = aktie 并且 col2 是任何东西 BUT "ja" ,我只想让转换返回 4 。在一般情况下,它应该是映射表 col2 中的其他值以外的任何值。我希望我现在解释得更好?

标签: sql wildcard


【解决方案1】:

这是使用 CTE 执行此操作的一种方法(适用于 SQL Server 和 Oracle)

WITH Map3 as 
( -- TRANSFORM WILL BE NULL IF A MATCH WAS NOT MADE
   SELECT T1.COL1, T1.COL2, T1.COL3, M.TRANSFORM
   FROM Table1 T1
   LEFT JOIN Mapping M ON T1.COL1 = M.COL1 
                      AND T1.COL2 = M.COL2
                      AND T1.COL3 = COALESCE(M.COL3, T1.COL3)
), Map2 as
(
   SELECT T1.COL1, T1.COL2, T1.COL3, COALESCE(T1.TRANSFORM,M.TRANSFORM)
   FROM Map3 T1
   LEFT JOIN Mapping M ON T1.COL1 = M.COL1 
                      AND T1.COL2 = COALESCE(M.COL2, T1.COL2)
                      AND T1.TRANSFORM IS NULL
)
SELECT * 
FROM Map2

我相信它的工作原理以及如何将其“扩展”到更多列是很清楚的。


如果您不能使用 CTE,这在功能上是相同的,可以任意嵌套:

SELECT T1.COL1, T1.COL2, T1.COL3, COALESCE(T1.TRANSFORM,M.TRANSFORM)
FROM (
   -- TRANSFORM WILL BE NULL IF A MATCH WAS NOT MADE
   SELECT T1.COL1, T1.COL2, T1.COL3, M.TRANSFORM
   FROM Table1 T1
   LEFT JOIN Mapping M ON T1.COL1 = M.COL1 
                      AND T1.COL2 = M.COL2
                      AND T1.COL3 = COALESCE(M.COL3, T1.COL3)
) T1
LEFT JOIN Mapping M ON T1.COL1 = M.COL1 
                   AND T1.COL2 = COALESCE(M.COL2, T1.COL2)
                   AND T1.TRANSFORM IS NULL

【讨论】:

  • 嗨 Hogan,它适用于我给出的示例,但我希望它能够正常工作。如果我将映射表中的第二行更改为 NULL NULL 2 我希望它匹配源表的第三行。在您的示例中,col1 周围没有合并。我试图在 col1 周围应用合并,但这不起作用,这是否意味着我必须为 N 列编写 Map2、Map3、...ManN 才能工作?对于具有许多列和行的表,这将如何执行?
  • @codermaden 是的,您需要 1-n 个子查询....想不出更好的方法。
  • 废话。好吧,这就是我试图避免的,但如果你得出同样的结论——而且没有其他人想出更好的解决方案——我想这是唯一的方法。幸运的是,它可以生成,所以现在我只是想知道对于具有 100,000 行和例如 50 列的源表来说,它的效率会有多高……我想压力测试会告诉你:)谢谢你的帮助,霍根!
  • 让我考虑一下.. 在这些尺寸下,我可能会提出一些优化建议....
  • 好吧,定义小 :) 它可能会有所不同,但通常会少于 1000 行。有 10,000 行的示例 - 但如果性能被杀死,可能会以其他方式处理
猜你喜欢
  • 2020-03-14
  • 1970-01-01
  • 2011-01-30
  • 1970-01-01
  • 2017-02-23
  • 2017-03-19
  • 1970-01-01
  • 2019-03-05
  • 1970-01-01
相关资源
最近更新 更多