【问题标题】:SQL Server query for multiple conditions on the same columnSQL Server 查询同一列上的多个条件
【发布时间】:2021-12-13 19:13:33
【问题描述】:

这是我正在使用的架构和数据

CREATE TABLE tbl (
    name varchar(20) not null,
    groups int NOT NULL
);

insert into tbl values('a', 35);
insert into tbl values('a', 36);
insert into tbl values('b', 35);
insert into tbl values('c', 36);
insert into tbl values('d', 37);
| name | groups|
|------|-------|
| a    | 35    |
| a    | 36    |
| b    | 35    |
| c    | 36    |
| d    | 37    |

现在我只需要组大于或等于 35 的名称 但还有一点是,当相应的组=36 也存在时,我只能包含组=35 的行

| name | groups|
|------|-------|
| a    | 35    |
| a    | 36    |

第二个条件是它可以包含那些组大于或等于 36 而没有组=35的名称

| name | groups|
|------|-------|
| c    | 36    |
| d    | 37    |

它应该省略的唯一情况是记录只有groups=35而没有相应的groups=36

| name | groups|
|------|-------|
| b    | 35    |

我已经尝试了以下

select name from tbl
where groups>=35
group by name
having count(distinct(groups))>=2
or groups>=36;

这是我面临的错误Column 'tbl.groups' is invalid in the HAVING clause because it is not contained in either an aggregate function or the GROUP BY clause.

【问题讨论】:

    标签: sql sql-server ssms ssms-2014


    【解决方案1】:

    所以,据我所知,您只想限制组 35 本身的位置。我想,让我们尝试隔离那些只有 groups=35 的名称,然后从那里不存在。这是你追求的正确输出吗?

    此外,在 where 子句中使用复杂的 OR 通常会导致您的查询不是 SARGable。最好使用 UNION 或一些如何构建查询,以便每个部分都可以使用索引(如果可以的话)。

    if object_id('tempdb..#tbl') is not null drop table #tbl;
    CREATE TABLE #tbl (
        name varchar(20) not null,
        groups int NOT NULL
    );
    
    insert into #tbl values('a', 35), ('a', 36), ('b', 35), ('c', 36), ('d', 37);
    
    select * 
    from #tbl tbl
    WHERE NOT EXISTS
        (
            SELECT COUNT(groups), name 
            FROM #tbl t
            WHERE EXISTS
                (
                    SELECT name 
                    FROM #tbl tb
                    WHERE groups = 35
                    and tb.name=t.name
                )
            AND t.name = tbl.name
            GROUP BY name 
            HAVING COUNT(groups)=1
        )
    ;
    

    【讨论】:

    • 上面缺少一个groups >= 35 条件来排除像('e', 31); 这样的行
    【解决方案2】:

    看起来你需要一个 exists() 条件。试试:

    select *
    from tbl t
    where t.groups >= 35
    and (
        t.groups > 35
        or exists(select * from tbl t2 where t2.name = t.name and t2.groups = 36)
    )
    

    还有其他方法可以安排 where 子句来达到相同的效果。预先设置 t.groups >= 35 条件应该使查询优化器能够利用组上的索引。

    【讨论】:

      【解决方案3】:

      您可以为此使用窗口计数

      这样可以避免多次加入表格

      SELECT
        name,
        groups
      FROM (
          SELECT *,
            Count36 = COUNT(CASE WHEN groups = 36 THEN 1 END) OVER (PARTITION BY name)
          FROM tbl
          WHERE groups >= 35
      ) tbl
      WHERE groups >= 36 OR Count36 > 0;
      

      db<>fiddle

      【讨论】:

        【解决方案4】:

        试试这个:

        DECLARE @tbl table ( [name] varchar(20) not null, groups int NOT NULL );
        
        INSERT INTO @tbl VALUES
            ('a', 35), ('a', 36), ('b', 35), ('c', 36), ('d', 37);
        
        DECLARE @group int = 35;
        
        ; WITH cte AS (
            SELECT
                [name]
                , COUNT ( DISTINCT groups ) AS distinct_group_count
            FROM @tbl
            WHERE
                groups >= @group
            GROUP BY
                [name]
        )
        SELECT t.* FROM @tbl AS t
        INNER JOIN cte
            ON t.[name] = cte.[name]
        WHERE
            cte.distinct_group_count > 1
            OR t.groups > @group;
        

        返回

        +------+--------+
        | name | groups |
        +------+--------+
        | a    |     35 |
        | a    |     36 |
        | c    |     36 |
        | d    |     37 |
        +------+--------+
        

        基本上,这会将名称结果限制为值 >= 35 且关联多个不同组的组,或组值大于 35 的任何名称。我们对您的数据做了一些假设,但我相信逻辑仍然适用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-04-12
          相关资源
          最近更新 更多