Select 行在某些相关行中具有大于零的特定列
Select rows with a specific column greater than zero in some related rows
我在 Postgres 9.5 数据库中有以下 table:
product_days
Column | Type | Modifiers
-----------+---------+----------------------------------------------
id | integer | not null default nextval('product_days_id_seq'::regclass)
days_id | integer |
product_id | integer |
available | integer |
price | integer |
Indexes:
"pk_product_days" PRIMARY KEY, btree (id)
"idx_product_days" btree (days_id)
"idx_product_days_0" btree (product_id)
Foreign-key constraints:
"product_days_days_id_fkey" FOREIGN KEY (days_id) REFERENCES days(id)
product
Column | Type | Modifiers
----------------+-----------------------------+-----------------------------------------------------
id | integer | not null default nextval('product_id_seq'::regclass)
name | character varying(100) |
number_of_items | integer |
created_at | timestamp without time zone | default now()
updated_at | timestamp without time zone | default now()
total_number | integer |
Indexes:
"pk_product" PRIMARY KEY, btree (id)
product_days.product_id
是外键,指的是product
,table,available
表示每天的商品数量。
我想获取特定日期 (days_id between 5 and 10
) 的所有可用产品 (available > 0
)。它们应该在这些天的 所有 有售。
目前我正在尝试通过以下查询获得结果,但我不确定它是否正确或这是最有效的方法:
select product.id as p_id, product.name as p_name, product.number_of_items as items
from product_days join product ON product_days.product_id = product.id
WHERE product_days.available > 0
AND prodcut_days.days_id between 5 and 10
group by product.id
HAVING count(*) > 5;
输出应该是这样的:
p_id | p_name | items
-------+-----------+-------
1 | product_1 | 4
2 | product_2 | 13
我需要在 SQL 或 plpgsql 中 运行 此查询的最有效方式。
澄清拼写错误后,是的,查询应该按照您的描述执行。
这是等价的并且更快一点:
SELECT id AS p_id, name AS p_name, number_of_items AS items
FROM (
SELECT product_id AS id
FROM product_days
WHERE available > 0
AND days_id BETWEEN 5 AND 10
GROUP BY 1
HAVING count(*) > 5
) d
JOIN product p USING (id);
应该有一个 UNIQUE
constraint 强制每个产品和一天最多 1 个条目:
ALTER TABLE product_days ADD CONSTRAINT your_name_here UNIQUE (product_id, days_id);
相关:
- Using a column in sql join without adding it to group by clause
优化读取性能
如果您需要优化此特定查询的性能并且有多个不合格的行(不是 available
或 day_id
不匹配),最锋利的武器是 partial index:
CREATE INDEX idx_name_here ON product_days (product_id)
WHERE available > 0
AND days_id BETWEEN 5 AND 10;
如果您有 autovacuum
运行 并且 table 上的写入负载不是太大,您应该会看到该索引上的仅索引扫描。
虽然使用了此索引(或更通用的索引),但此查询应该更快:
SELECT id AS p_id, name AS p_name, number_of_items AS items
FROM (
SELECT product_id AS id
FROM product_days d5
JOIN product_days d6 USING (product_id)
JOIN product_days d7 USING (product_id)
JOIN product_days d8 USING (product_id)
JOIN product_days d9 USING (product_id)
JOIN product_days d10 USING (product_id)
WHERE d5.days_id = 5 AND d5.available > 0
AND d6.days_id = 6 AND d6.available > 0
AND d7.days_id = 7 AND d7.available > 0
AND d8.days_id = 8 AND d8.available > 0
AND d9.days_id = 9 AND d9.available > 0
AND d10.days_id = 10 AND d10.available > 0
) d
JOIN product p USING (id);
因为这是一个relational-division的核心案例。参见:
- How to filter SQL results in a has-many-through relation
- Arbitrary queries on n:m relationship, including “all” and “any”
我在 Postgres 9.5 数据库中有以下 table:
product_days
Column | Type | Modifiers
-----------+---------+----------------------------------------------
id | integer | not null default nextval('product_days_id_seq'::regclass)
days_id | integer |
product_id | integer |
available | integer |
price | integer |
Indexes:
"pk_product_days" PRIMARY KEY, btree (id)
"idx_product_days" btree (days_id)
"idx_product_days_0" btree (product_id)
Foreign-key constraints:
"product_days_days_id_fkey" FOREIGN KEY (days_id) REFERENCES days(id)
product
Column | Type | Modifiers
----------------+-----------------------------+-----------------------------------------------------
id | integer | not null default nextval('product_id_seq'::regclass)
name | character varying(100) |
number_of_items | integer |
created_at | timestamp without time zone | default now()
updated_at | timestamp without time zone | default now()
total_number | integer |
Indexes:
"pk_product" PRIMARY KEY, btree (id)
product_days.product_id
是外键,指的是product
,table,available
表示每天的商品数量。
我想获取特定日期 (days_id between 5 and 10
) 的所有可用产品 (available > 0
)。它们应该在这些天的 所有 有售。
目前我正在尝试通过以下查询获得结果,但我不确定它是否正确或这是最有效的方法:
select product.id as p_id, product.name as p_name, product.number_of_items as items
from product_days join product ON product_days.product_id = product.id
WHERE product_days.available > 0
AND prodcut_days.days_id between 5 and 10
group by product.id
HAVING count(*) > 5;
输出应该是这样的:
p_id | p_name | items
-------+-----------+-------
1 | product_1 | 4
2 | product_2 | 13
我需要在 SQL 或 plpgsql 中 运行 此查询的最有效方式。
澄清拼写错误后,是的,查询应该按照您的描述执行。
这是等价的并且更快一点:
SELECT id AS p_id, name AS p_name, number_of_items AS items
FROM (
SELECT product_id AS id
FROM product_days
WHERE available > 0
AND days_id BETWEEN 5 AND 10
GROUP BY 1
HAVING count(*) > 5
) d
JOIN product p USING (id);
应该有一个 UNIQUE
constraint 强制每个产品和一天最多 1 个条目:
ALTER TABLE product_days ADD CONSTRAINT your_name_here UNIQUE (product_id, days_id);
相关:
- Using a column in sql join without adding it to group by clause
优化读取性能
如果您需要优化此特定查询的性能并且有多个不合格的行(不是 available
或 day_id
不匹配),最锋利的武器是 partial index:
CREATE INDEX idx_name_here ON product_days (product_id)
WHERE available > 0
AND days_id BETWEEN 5 AND 10;
如果您有 autovacuum
运行 并且 table 上的写入负载不是太大,您应该会看到该索引上的仅索引扫描。
虽然使用了此索引(或更通用的索引),但此查询应该更快:
SELECT id AS p_id, name AS p_name, number_of_items AS items
FROM (
SELECT product_id AS id
FROM product_days d5
JOIN product_days d6 USING (product_id)
JOIN product_days d7 USING (product_id)
JOIN product_days d8 USING (product_id)
JOIN product_days d9 USING (product_id)
JOIN product_days d10 USING (product_id)
WHERE d5.days_id = 5 AND d5.available > 0
AND d6.days_id = 6 AND d6.available > 0
AND d7.days_id = 7 AND d7.available > 0
AND d8.days_id = 8 AND d8.available > 0
AND d9.days_id = 9 AND d9.available > 0
AND d10.days_id = 10 AND d10.available > 0
) d
JOIN product p USING (id);
因为这是一个relational-division的核心案例。参见:
- How to filter SQL results in a has-many-through relation
- Arbitrary queries on n:m relationship, including “all” and “any”