【问题标题】:T-SQL - Value does not match at least one item in listT-SQL - 值与列表中的至少一项不匹配
【发布时间】:2014-07-24 21:18:06
【问题描述】:

我有一个 MS SQL 表,其中包含多个列,每个列代表不同的员工角色,每个列对应的列指示该角色的当前员工是否处于活动状态、休假、终止等。由于依赖于外部数据来源,这些字段并没有真正标准化。

我正在寻找运行查询以返回至少一个“活动”列等于“活动”的所有行。除了 Active 之外,还有几个可能的值。

我知道这样做的较长方法之一是

SELECT * FROM MYTABLE
WHERE IsActive1 <> 'ACTIVE' OR
      IsActive2 <> 'ACTIVE' OR
      IsActive3 <> 'ACTIVE' OR
      IsActive4 <> 'ACTIVE' OR
... etc

只是想知道是否有更短的方法,可能更有效的方法来做到这一点。我已经看到很多解决方案可以跨多个列查找匹配项,但没有一种解决方案可以查找不匹配项。

【问题讨论】:

  • 你为什么要长一点?您没有列出数据库引擎,但是在此查询上使用索引会很困难。除非你的数据库引擎支持一些技巧,比如 SQL Server 的过滤索引之类的,否则无论你做什么,你都会进行表扫描。
  • 列可以包含NULL 值吗?
  • MS SQL Server db 引擎隐含在标题 (TSQL) 中。我在问我的路是否比其他路更长。告诉我你所说的这些 SQL Server 技巧:)
  • @GordonLinoff 是的,NULL 是可能的
  • 我不确定您使用的是哪个版本的 SQL Server。如果是 2008 年或更高版本,请查看过滤索引,它们基本上是具有 where 子句的索引。然后,您将拥有一个仅包含“非活动”行的索引

标签: sql-server tsql select


【解决方案1】:

我不知道这更优雅,但你可以这样做:

where replace(IsActive1 + IsActive2 + IsActive3 + . . .,
              'ACTIVE', '') <> ''

请注意,如果值可能是NULL,那么您需要将它们替换为其他值。此外,这假设字符串本身不为空。

编辑:

如果您想高效地执行此操作并且希望代码看起来不错,请添加计算列并在该列上建立索引:

alter table mytable
    add IsAllActive as (case when IsActive1 = 'ACTIVE' and
                                  IsActive2 = 'ACTIVE' and
                                  . . .
                             then 'ACTIVE'
                             else 'INACTIVE'
                        end);

create index mytable_IsAllActive on mytable(IsAllActive);

您可能还想将其他相关列添加到索引中。

【讨论】:

  • 去研究过滤索引只是为了熟悉它们,但计算列似乎是解决我的问题的方法
【解决方案2】:

尝试使用过滤索引,它可以包含在WHERE 子句中的内容非常有限:

--simulate your existing table
create table Test (RowID int identity(1,1) primary key
                  ,c1 varchar(10), c2 varchar(10),c3 varchar(10),c4 varchar(10)
                  )
go
--simulate your existing data
INSERT INTO Test VALUES ('ACTIVE','ACTIVE','ACTIVE','ACTIVE')        --1
                      , ('ACTIVE','ACTIVE','ACTIVE','ACTIVE')        --2  
                      , ('ACTIVE','ACTIVE','ACTIVE','ACTIVE')        --3
                      , ('INACTIVE','ACTIVE','ACTIVE','ACTIVE')      --4 <Inactive
                      , ('ACTIVE','INACTIVE','ACTIVE','ACTIVE')      --5 <Inactive
                      , ('ACTIVE','ACTIVE','INACTIVE','ACTIVE')      --6 <Inactive
                      , ('ACTIVE','ACTIVE','ACTIVE','INACTIVE')      --7 <Inactive
                      , ('INACTIVE','ACTIVE','ACTIVE','INACTIVE')    --8 <Inactive
                      , ('INACTIVE','ACTIVE','INACTIVE','INACTIVE')  --9 <Inactive
                      , ('INACTIVE','INACTIVE','INACTIVE','INACTIVE')--10<Inactive
go

--what you need to add to your existing table
CREATE NONCLUSTERED INDEX fIX_Test_c1 ON Test(c1) WHERE C1 !='ACTIVE'
CREATE NONCLUSTERED INDEX fIX_Test_c2 ON Test(c2) WHERE C2 !='ACTIVE'
CREATE NONCLUSTERED INDEX fIX_Test_c3 ON Test(c3) WHERE C3 !='ACTIVE'
CREATE NONCLUSTERED INDEX fIX_Test_c4 ON Test(c4) WHERE C4 !='ACTIVE'

--your query o get the data
      SELECT * FROM Test WHERE c1 !='ACTIVE'
UNION SELECT * FROM Test WHERE c2 !='ACTIVE'
UNION SELECT * FROM Test WHERE c3 !='ACTIVE'
UNION SELECT * FROM Test WHERE c4 !='ACTIVE'

您可以尝试使用索引的持久计算列:

ALTER TABLE Test ADD C_Summary AS (LEFT(ISNULL(c1,'Z'),1)+LEFT(ISNULL(c1,'Z'),1)+LEFT(ISNULL(c1,'Z'),1)+LEFT(ISNULL(c1,'Z'),1))
go
CREATE NONCLUSTERED INDEX fIX_Test_C_Summary ON Test(C_Summary) 
go

select * from test WHERE C_Summary !='AAAA'

您必须在数据上尝试这些(和其他变体),这可能会影响索引的使用。此外,您可能想尝试更改表设计,将所有 C1、C2、C3、C4、... 列拆分为子表中的不同行。然后,您可以对该表执行单个过滤索引。

【讨论】:

    猜你喜欢
    • 2011-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-18
    • 1970-01-01
    • 2019-03-11
    相关资源
    最近更新 更多