SQL查询以搜索可以位于三个不同表中的唯一ID

SQL查询以搜索可以位于三个不同表中的唯一ID,sql,postgresql,postgresql-8.1,Sql,Postgresql,Postgresql 8.1,我有三个控制产品、颜色和尺寸的表格。产品可以有或没有颜色和尺寸。颜色可以有大小,也可以没有大小 product color size ------- ------- ------- id id id unique_id id_product (FK from product) id_pro

我有三个控制产品、颜色和尺寸的表格。产品可以有或没有颜色和尺寸。颜色可以有大小,也可以没有大小

product      color                           size
-------      -------                         -------
id           id                              id
unique_id    id_product (FK from product)    id_product (FK from version)
stock        unique_id                       id_version (FK from version)
title        stock                           unique_id
                                             stock
所有表中都存在的
unique\u id
列是一种串行类型(自动递增),其计数器与三个表共享,基本上作为它们之间的全局唯一id

它工作得很好,但是当我必须在
unique\u id
中选择一些字段时,我试图提高查询性能

由于我不知道我要查找的
唯一\u id
在哪里,我使用的是
UNION
,如下所示:

select title, stock
from product 
where unique_id = 10

UNION

select p.title, c.stock
from color c
join product p on c.id_product = p.id
where c.unique_id = 10

UNION

select p.title, s.stock
from size s
join product p on s.id_product = p.id
where s.unique_id = 10;
有更好的方法吗?谢谢你的建议

编辑1

基于@ErwinBrandstetter和@ErikE的回答,我决定使用下面的查询。主要原因是:

1) 由于
unique\u id
在所有表中都有索引,我将获得良好的性能

2) 使用
unique\u id
我将找到产品代码,这样我就可以使用另一个简单的连接获得我需要的所有列

SELECT 

    p.title,
    ps.stock

FROM (

    select id as id_product, stock
    from product 
    where unique_id = 10

    UNION

    select id_product, stock
    from color
    where unique_id = 10

    UNION

    select id_product, stock
    from size
    where unique_id = 10

) AS ps

JOIN product p ON ps.id_product = p.id;

有一种更简单的方法可以使用三个单独的自动增量列生成唯一ID。只需在ID前加上一封信,即可将其取消资格:

颜色:

 C0000001
 C0000002
 C0000003
尺寸:

 S0000001
 S0000002
 S0000003
 ...
产品:

 P0000001
 P0000002
 P0000003
 ...
有几个优点:

  • 您不需要跨表序列化ID的创建以确保唯一性。这将提供更好的性能
  • 实际上,您不需要将信存储在表中。同一个表中的所有ID都以相同的字母开头,因此您只需要存储数字。这意味着您可以使用普通的
    auto_increment
    列来生成ID
  • 如果你有一个ID,你只需要检查第一个字符,看看它可以在哪个表中找到。如果只想知道是产品ID还是尺寸ID,甚至不需要对数据库进行查询
缺点:

  • 它不再是一个数字。但是你可以用1,2,3代替C,S,P来解决这个问题
PL/pgSQL函数 要解决当前的问题,类似以下的plpgsql函数应该更快:

CREATE OR REPLACE FUNCTION func(int)
  RETURNS TABLE (title text, stock int) LANGUAGE plpgsql AS
$BODY$
BEGIN

RETURN QUERY
SELECT p.title, p.stock
FROM   product p
WHERE  p.unique_id = $1; -- Put the most likely table first.

IF NOT FOUND THEN
    RETURN QUERY
    SELECT p.title, c.stock
    FROM   color c
    JOIN   product p ON c.id_product = p.id
    WHERE  c.unique_id = $1;
END;

IF NOT FOUND THEN
    RETURN QUERY
    SELECT p.title, s.stock
    FROM   size s
    JOIN   product p ON s.id_product = p.id
    WHERE  s.unique_id = $1;
END IF;

END;
$BODY$;
更新了具有表限定列名的函数,以避免与
OUT
参数的命名冲突

返回表
需要PostgreSQL 8.4,
返回查询
需要8.2版。您可以将这两个版本都替换为旧版本

不言而喻,您需要为每个相关表的列
unique\u id
编制索引<代码>id应自动编入索引,作为主键


重新设计 理想情况下,您可以仅从ID判断哪个表。您可以继续使用一个公共序列,但第一个表添加
100000000
,第二个表添加
200000000
,第三个表添加
300000000
,或者添加任何适合您需要的内容。这样,数字的最低有效部分很容易区分

纯整数的范围是从-2147483648到+2147483647的数字,如果这还不够,请移动到。如果可能的话,我会坚持使用
integer
id。它们比
bigint
text
更小更快


CTEs(实验!) 如果由于某种原因无法创建函数,此纯SQL解决方案可能会执行类似的操作:

WITH x(uid) AS (SELECT 10) -- provide unique_id here
    , a AS (
    SELECT title, stock
    FROM   x, product 
    WHERE  unique_id = x.uid
    )
    , b AS (
    SELECT p.title, c.stock
    FROM   x, color c
    JOIN   product p ON c.id_product = p.id
    WHERE  NOT EXISTS (SELECT 1 FROM a)
    AND    c.unique_id = x.uid
    )
    , c AS (
    SELECT p.title, s.stock
    FROM   x, size s
    JOIN   product p ON s.id_product = p.id
    WHERE  NOT EXISTS (SELECT 1 FROM b)
    AND    s.unique_id = x.uid
    )
SELECT * FROM a
UNION ALL
SELECT * FROM b
UNION ALL
SELECT * FROM c;
我不确定它是否能像我希望的那样避免额外的扫描。必须进行测试。此查询至少需要PostgreSQL 8.4


升级 正如我刚刚了解到的,OP在PostgreSQL 8.1上运行。
单独升级将大大加快操作速度


查询PostgreSQL 8.1 由于您的选项有限,并且无法使用plpgsql函数,因此此函数的性能应该比您现有的函数更好。测试-v8.1中提供

SELECT title, stock
FROM   product 
WHERE  unique_id = 10

UNION ALL
SELECT p.title, ps.stock
FROM   product p
JOIN  (
    SELECT id_product, stock
    FROM   color
    WHERE  unique_id = 10

    UNION ALL
    SELECT id_product, stock
    FROM   size
    WHERE  unique_id = 10
    ) ps ON ps.id_product = p.id;

只要在每个表的
unique\u id
上有一个索引,并且在连接列上有索引,您的查询就会非常高效


您可以将那些
UNION
转换为
UNION ALL
,但是对于这个查询,在性能上不会有任何差异。

我认为是重新设计的时候了

您使用的东西作为项目的条形码,它们在一个方面基本相同(它们是SerialNumberItems),但由于在其他方面不同而被拆分为多个表

我有几个想法给你:

更改默认值 只需使每个产品都有一种颜色“无颜色”和一种尺寸“无尺寸”。然后,您可以查询任何您想要查找所需信息的表

超类型/子类型 无需太多修改,就可以使用超类型/子类型数据库设计模式

其中有一个父表,所有不同的详细程度标识符都位于该表中,子类型表的共享列位于超类型表中(所有项的方式相同)。对于项目的不同方式,有一个子类型表。如果需要子类型的互斥性(可以有颜色或大小,但不能两者都有),则会为父表提供一个TypeID列,并且子类型表对ParentID和TypeID都有一个FK。看看你的设计,事实上你不会使用相互排他性

如果使用超类型表的模式,则确实存在必须插入两部分的问题,首先插入超类型,然后插入子类型。删除也需要按相反顺序删除。但是,通过一个查询就可以从supertype表中获取基本信息,如标题和库存,这给您带来了巨大的好处

您甚至可以为每个子类型创建模式绑定视图,使用instead of触发器将插入、更新和删除转换为基表+子表上的操作

更大的重新设计 你可以完全改变颜色和尺寸与产品的关系

首先,你的p
SELECT p.title
        , COALESCE (p2.stock, c.stock, s.stock) AS stock
FROM product p
LEFT JOIN product p2 on p2.id = p.id AND p2.unique_id = 10
LEFT JOIN color c on c.id_product = p.id AND c.unique_id = 10
LEFT JOIN zsize s on s.id_product = p.id AND s.unique_id = 10
WHERE COALESCE (p2.stock, c.stock, s.stock) IS NOT NULL
        ;