匹配全部或全部的SQL

时间:2012-11-21 09:13:02

标签: sql postgresql select

我在PostgreSQL数据库中有以下表格:

CREATE TABLE maclist (
    username character varying,
    mac macaddr NOT NULL,
    CONSTRAINT maclist_user_mac_key UNIQUE (username, mac)
);

我需要一种方法来检查MAC地址是否已分配给用户,或者用户根本没有分配的MAC地址。基本上我需要一个查询,如果所有条件都为真或没有条件为真,则返回行,即不是XOR b

修改 例如:

username | mac
john     | 11:22:33:44:55:66
john     | 11:22:33:44:55:67
doe      | 11:22:33:44:55:68

如果我查询:

username = john, mac = 11:22:33:44:55:66                     -> true, 1 whatever...
username = john, mac != 11:22:33:44:55:66                    -> 0, null or nothing...
username = jane, mac = no matter what except john's or doe's -> true, 1 whatever...
username = jane, mac = john's or doe's                       ->  0, null or nothing...

我在两个条件下需要true

  1. 该(用户,mac)组合有一行。没有行
  2. 对于该用户而且该mac没有行
  3. 到目前为止,我得到了这个:

    SELECT yes
    FROM
      (SELECT 1 AS yes) AS dummy
    LEFT JOIN maclist ON (username = 'user'
                          OR mac = '11:22:33:44:55:66')
    WHERE ((username = 'user'
            AND mac = '11:22:33:44:55:66')
           OR (username IS NULL
               AND mac IS NULL);
    

    它可以工作,但在我看来像是一个黑客,我也不知道随着数据库的增长这个查询的性能。 我的问题是,有没有更好的方法来做到这一点。

3 个答案:

答案 0 :(得分:3)

编辑:我稍微修改了逻辑,我认为它符合您的要求

以下代码将在两个条件下返回1 ...

  • usermac
  • 有一行
  • 该用户 0行,0
  • mac

在所有其他条件下,查询返回0

  • user有一些行,但没有一行用于mac
  • mac有一些行,但没有一行用于user


SELECT
  CASE WHEN COUNT(*) = 0                                THEN 1
       WHEN SUM(CASE WHEN mac = '11:22:33:44:55:66'
                      AND user = 'user' THEN 1
                                        ELSE 0 END) = 1 THEN 1
                                                        ELSE 0
  END
FROM
  maclist
WHERE
     username = 'user'
  OR mac      = '11:22:33:44:55:66'

答案 1 :(得分:1)

您可以将查询编写为:

prepare query_mac(text, macaddr) as
select exists (select 1 from maclist where username = $1 and mac = $2)
       or (not exists (select 1 from maclist where username = $1)
           and not exists (select 1 from maclist where mac = $2)
          );

PostgreSQL将分别评估三个查询中的每一个,并在前两个(username,mac)上使用唯一索引。如果需要,您可以在(mac)上为第三个添加索引。

答案 2 :(得分:0)

实际上,当涉及OR时,所有数据库都不会使用索引,因此请避免使用连接中的索引。

编辑:

您似乎真的在进行两个单独的查询:

  • 谁是分配给(如果有的话)的mac
  • 为用户分配了什么mac(如果有的话)

所以联合不会像子选择集合那么容易。使用单独的查询将信息拆分为自己的简单查询,并将它们合并为一个:

select
    (select username from maclist
     where mac = '11.22:33:44:55:66') as mac_assigned_to,
    (select mac from maclist
     where username = 'user') as user_assigned_to

如果当前没有分配任何内容,则两列都可能为空,否则它们将显示为每个关注点分配的内容。