【问题标题】:Iterating through table columns until null value遍历表列直到空值
【发布时间】:2017-07-06 17:19:28
【问题描述】:

我正在通过 Access 2010 处理 SQL Server 上的一些数据,我无法灵活更改,只能进行查询。每个单独的实体都有一个 ID,并且可能有一个跟随它的属性列表,没有特定的最大或最小条目。创建此数据集的人的解决方案是创建 37 个不同的属性列集。对于每个属性,有 4 列(尽管只有 2 列有用)。实际上,有 148 列表示可能有 37 个项目,尽管第 3 组或第 4 组之后的大多数列都是 NULL。虽然有一些一致性 - 值首先进入第一列组。只要任何一列具有空值,就可以假定它之后的每一列也是空的。如果第一列为空,则它没有属性。数据看起来有点像这样:

| ID | Attr1 | Code1 | Attr2 | Code2 | Attr3 | Code3 | |----|-------|-------|--------|--------|--------|--------| | 1 | Foo | 2 | Bar | 1 | (null) | (null) | | 2 | Bar | 2 | (null) | (null) | (null) | (null) | | 3 | Bar | 1 | Foo | 1 | Bar | 2 |

对此进行查询的最佳方法是什么,以便如果有人想要 CodeX=1(X 是任意数字),即使该行的 Code1=1 或 Code37=1,查询也会返回结果。我考虑过这样做:

A.Mod1=1
Or A.Mod2=1
Or A.Mod3=1
Or A.Mod4=1
...

但这看起来很可怕,就像山洪一样干燥。如果我想同时针对属性和代码,这变得特别可怕,我可能会这样做。我简要地查看了 CASE,希望我能想出一种方法来迭代并带回第一个空值的值,但是我的草稿没有多大意义,而且同样混乱/沉重嵌套。

除了上面建议的非常丑陋的方法之外,关于如何解决这些问题的任何想法?

【问题讨论】:

  • 你能做直通查询吗?对列使用 COALESCE 关键字以相反的顺序获取列列表中的第一个非空值
  • 我可以做直通,是的。那让我快速看一下。
  • 好的,所以这越来越接近我的需要了。我想要执行的步骤之一是能够返回所有非空值,这样可以节省一些输入。我还希望能够匹配特定值,这意味着我每次都需要写出表达式以进行合并,因为“NOT NULL”不够有意义。然后我又回到了我的 OR 语句开始的地方。
  • 如果您可以在 SQL Server 中创建视图,请创建一个以规范化数据结构,然后针对该视图进行查询。您也可以将该视图创建为 Access 查询,但性能可能会差很多。
  • 很遗憾,无法将视图添加到服务器。我们只读了服务器端表,但我们可以制作本地表。我能否将列转置为具有视图的行,将每个属性映射到它自己的行?

标签: sql sql-server loops ms-access ms-access-2010


【解决方案1】:

这里有两种方法可以取消透视数据。这样做,您只需为参数添加 where 子句。 IE。只需将where code = 1 添加到第一个查询或将where codes = 1 添加到第二个查询。如有必要,您也可以将其转回,但这会使它正常化。正如你所说,你可以用一个疯狂的长 CASE 语句来做到这一点,但它又枯燥又冗长。

declare @table table(ID int, Attr1 varchar(16), Code1 varchar(16),Attr2 varchar(16), Code2 varchar(16), Attr3 varchar(16), Code3 varchar(16))
insert into @table
values
(1,'Foo',2,'Bar',1,NULL,NULL),
(2,'Bar',2,NULL,NULL,NULL,NULL),
(3,'Bar',1,'Foo',1,'Bar',2)

select
    t.ID, 
    c.Attribute,
    c.Code
from @table t
cross apply (
            select Attr1, Code1 union all
            select Attr2, Code2 union all
            select Attr3, Code3 
            ) c (Attribute, Code)
where
    Attribute is not null


select
    ID,
    Attributes,
    Codes
from(
     select ID, Attr1, Code1, Attr2, Code2, Attr3, Code3 
     from @table) main
unpivot
(Attributes for Attribute in (Attr1, Attr2, Attr3)) attr
unpivot
(Codes for Code in (Code1, Code2, Code3)) cod
where
    right(Attribute,1) = right(Code,1)

在性能方面,取消透视数据可能会更快,但让优化器使用下面的逻辑和简单的布尔比较

declare @Code int = 2
declare @Attr varchar(64) = null

select
    t.*
from @table t
where
    (Code1 = @Code or
     Code2 = @Code or
     Code3 = @Code or
     @Code is null)
     and
     (Attr1 = @Attr or
      Attr2 = @Attr or
      Attr3 = @Attr or
      @Attr is null)

【讨论】:

  • 我大致了解您的用途,这很有帮助。就执行而言,这会比 OR 语句组合的 37 个条件更好吗?我担心在已经很大的数据库上执行这样的复杂操作会严重影响我的性能。我的代码将主要在后台运行,用户输入将定义正在搜索的代码和/或属性,因此最终用户性能是重中之重。不幸的是,下载该表的本地修改副本也将无法达到目的,因为该表几乎每月更新一次。
  • 好吧,无法判断哪个性能更好.. 这取决于您的结构、索引等。真正判断的唯一方法是在测试系统上或关闭期间进行两种测试小时或什么的。如果您运行它并且花费的时间超过几秒钟(或者您的阈值是多少),只需终止查询并尝试其他方式。老实说,大型布尔语句可能是最快的,尽管您的查询会很长,但它可能会表现良好。我会在底部更新。
  • @AndrewMcPheron 更新了答案。优化器几乎肯定会以最快的速度找到最后一个查询。您可以将任一参数设置为 null,也可以将这两个参数设置为 null。
  • 我不记得自从问了我的答案后改变了多少,但是好吧。
猜你喜欢
  • 1970-01-01
  • 2019-09-01
  • 2014-02-11
  • 1970-01-01
  • 1970-01-01
  • 2019-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多