Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/77.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql Postgres数组查询_Sql_Postgresql - Fatal编程技术网

Sql Postgres数组查询

Sql Postgres数组查询,sql,postgresql,Sql,Postgresql,(以下是对我的问题的高度简化描述。公司政策不允许我详细描述实际情况。) 涉及的数据库表包括: PRODUCTS: ID Name --------- 1 Ferrari 2 Lamborghini 3 Volvo CATEGORIES: ID Name ---------- 10 Sports cars 20 Safe cars 30 Red cars PRODUCTS_CATEGORIES ProductID CategoryID --

(以下是对我的问题的高度简化描述。公司政策不允许我详细描述实际情况。)

涉及的数据库表包括:

PRODUCTS:
ID   Name
---------
1    Ferrari
2    Lamborghini
3    Volvo


CATEGORIES:
ID    Name
----------
10    Sports cars
20    Safe cars
30    Red cars

PRODUCTS_CATEGORIES
ProductID    CategoryID
-----------------------
1            10
1            30
2            10
3            20

LOCATIONS:
ID      Name
------------
100     Sports car store
200     Safe car store
300     Red car store
400     All cars r us


LOCATIONS_CATEGORIES:
LocationID    CategoryID
------------------------
100           10
200           20
300           30
400           10
400           20
400           30
请注意,这些位置并不是直接连接到产品,而是连接到类别。客户应该能够看到一个位置列表,该列表可以提供他们想要购买的产品所属的所有产品类别。例如:

一位顾客想买一辆法拉利。这将在10类或30类商店中提供。这给了我们100、300和400家商店,但不是200家

但是,如果客户想要购买沃尔沃和兰博基尼,则可以从10类和20类商店购买。这只给了我们400号店

另一位客户想买一辆法拉利和一辆沃尔沃。他们可以从10+20类(运动型和安全型)或30+20类(红色和安全型)商店购买

我需要的是一个postgres查询,它接受许多产品,并返回可以找到它们的位置。我从数组开始,答案正在进行中:(我将在得到所需结果时添加答案)

关于你的第一个问题:

一位顾客想买一辆法拉利。这可以在商店买到 第10类或第30类。这给了我们100家、300家和400家店铺,但没有 200

第二个问题:

然而,如果客户想要购买沃尔沃和兰博基尼,这一点很重要 可从10类和20类商店购买。哪只 给我们400号店

使用INTERSECT的第二个问题的结果: intersect将交叉引用每次可找到1种产品的所有门店:

SELECT DISTINCT l.id, l.name
FROM Products p
LEFT JOIN Product_Categories p_c
ON p.id = p_c.ProductId
LEFT JOIN Categories c
ON p_c.CategoryId = c.id
LEFT JOIN Locations_Categories l_c
ON c.id = l_c.CategoryId
LEFT JOIN Locations l
ON l_c.LocationId = l.id
WHERE p.id = 2
INTERSECT
SELECT DISTINCT l.id, l.name
FROM Products p
LEFT JOIN Product_Categories p_c
ON p.id = p_c.ProductId
LEFT JOIN Categories c
ON p_c.CategoryId = c.id
LEFT JOIN Locations_Categories l_c
ON c.id = l_c.CategoryId
LEFT JOIN Locations l
ON l_c.LocationId = l.id
WHERE p.id = 3
对于每个新产品,您添加一个新的INTERSECT语句,并使用所需的产品id创建一个新的select
SQLFIDDLE:

在这里很难完全避免使用数组,但我想我找到了一个使用较少数组函数的解决方案

我没有选择需要的位置,而是排除了无效的位置

WITH needed_categories AS (
  SELECT p."ID", array_agg(pc."CategoryID") AS at_least_one_should_match
  FROM Products p
  JOIN Products_Categories pc ON p."ID" = pc."ProductID"
  WHERE p."ID" IN (1, 3)
  GROUP BY p."ID"
),
not_valid_locations AS (
  SELECT DISTINCT lc."LocationID", unnest(nc.at_least_one_should_match)
  FROM Locations_Categories lc
  JOIN needed_categories nc ON NOT ARRAY[lc."CategoryID"] && nc.at_least_one_should_match 
  EXCEPT
  SELECT * FROM Locations_Categories
) 
SELECT * 
FROM Locations
WHERE "ID" NOT IN (
  SELECT "LocationID" FROM not_valid_locations
);
以下是SQLFiddle:

这是可行的,但我仍在努力避免对
位置\u类别进行双序列扫描。

汽车可能属于多个类别这一事实有点棘手,我使用数组解决了这个问题,但我也在尝试摆脱这些问题。

以下是查询。您应该在(1,3)
中插入所选车辆ID的列表
pc.ProductId,最后您应该将条件更正为所选车辆计数,因此如果您选择1和3,您应该写入
HAVING count(DISTINCT pc.ProductId)=2
如果您选择3辆车,则必须有3辆车。
HAVING
中的此条件为您提供了所有车辆都位于这些位置的条件:

SELECT Id FROM Locations l
JOIN Locations_Categories lc on l.Id=lc.LocationId
JOIN Products_Categories pc on lc.CategoryId=pc.CategoryID
where pc.ProductId in (1,3)
GROUP BY l.id
HAVING COUNT(DISTINCT pc.ProductId) = 2

例如,对于一辆汽车,它将是:

SELECT Id FROM Locations l
JOIN Locations_Categories lc on l.Id=lc.LocationId
JOIN Products_Categories pc on lc.CategoryId=pc.CategoryID
where pc.ProductId in (1)
GROUP BY l.id
HAVING COUNT(DISTINCT pc.ProductId) = 1

(这基本上是对@valex的答案的详细阐述,尽管我在发布之前没有意识到这一点;请接受@valex不是这个答案)


这可以只使用连接和聚合来完成

按照常规,构建连接树,将位置映射到产品。然后将其与所需产品列表(一列值行)联接,并筛选联接以仅匹配产品名称。现在,您有一行显示了产品的位置,无论该产品位于何处

现在按地点和退货地点分组,在这些地点,现有产品的数量等于我们要寻找的数量(全部)。对于任何位置,我们省略了
HAVING
过滤器,因为连接返回的任何位置行都是我们想要的

因此:

基本上就是你想要的

对于“存储任何想要的产品”查询,请删除
HAVING
子句

如果要显示具有任何匹配项但基于匹配数排序的存储,则还可以使用“排序依据”
聚合

如果您想列出可在该商店找到的产品,还可以在
选择值列表中添加
字符串\u agg(p.“Name”)


如果希望输入是数组而不是值列表,只需将
值(…)
替换为
选择unest($1)
,并将数组作为参数
$1
传递,或者将其逐字写入此处不需要的
$1

数组,所有这些都可以通过乘法连接表来实现。我按照这些思路做了一些思考,但并没有真正把它们结合起来。你能帮我提供更多的细节吗?当然,如果你把你的样本数据放到一个数据库中,我会试一试。真的很感谢你的帮助:我开始认为这不是你想要的方式,我猜你想要一个查询来完成一切?虽然这种复杂程度可能有点超出我的想象,但这正是问题所在。理想情况下,我希望能够输入任意数量的产品到这个查询中,并获得所有产品都可以找到的位置。但也许这是不可能的。在这种情况下,也许只在应用层进行过滤会更好。你怎么想?所以你的输入总是一个产品列表?不是类别?您是在动态创建sql吗?理想情况下,它应该尽可能是静态的,但动态创建也很好。避免使用数组并不是真正必要的。数据库不是很大,这不是一个经常执行的查询。因此,性能并不重要。这是一个聪明的解决办法。我不会想到像那样向后做。好吧,很高兴听到这些,因为我一直在努力提高性能和摆脱阵列:)这太复杂了,不能移植到您的项目中吗?我认为应该可以。我现在正试图把它移植过来,我会让你知道它是怎么回事。我只是在发布了我的之后才正确地理解了这一点。这是和我之前写过的内容相同的方法,并且是在更早的时候发布的;我所做的一切都不同,只是映射了产品名称,这很简单,而且计数方式也略有不同。所以这应该优先于我的答案被接受。你应该使用
计数(不同的L.Id)
WITH needed_categories AS (
  SELECT p."ID", array_agg(pc."CategoryID") AS at_least_one_should_match
  FROM Products p
  JOIN Products_Categories pc ON p."ID" = pc."ProductID"
  WHERE p."ID" IN (1, 3)
  GROUP BY p."ID"
),
not_valid_locations AS (
  SELECT DISTINCT lc."LocationID", unnest(nc.at_least_one_should_match)
  FROM Locations_Categories lc
  JOIN needed_categories nc ON NOT ARRAY[lc."CategoryID"] && nc.at_least_one_should_match 
  EXCEPT
  SELECT * FROM Locations_Categories
) 
SELECT * 
FROM Locations
WHERE "ID" NOT IN (
  SELECT "LocationID" FROM not_valid_locations
);
SELECT Id FROM Locations l
JOIN Locations_Categories lc on l.Id=lc.LocationId
JOIN Products_Categories pc on lc.CategoryId=pc.CategoryID
where pc.ProductId in (1,3)
GROUP BY l.id
HAVING COUNT(DISTINCT pc.ProductId) = 2
SELECT Id FROM Locations l
JOIN Locations_Categories lc on l.Id=lc.LocationId
JOIN Products_Categories pc on lc.CategoryId=pc.CategoryID
where pc.ProductId in (1)
GROUP BY l.id
HAVING COUNT(DISTINCT pc.ProductId) = 1
WITH wantedproducts(productname) AS (VALUES('Volvo'), ('Lamborghini'))
SELECT l."ID"
FROM locations l
INNER JOIN locations_categories lc ON (l."ID" = lc."LocationID")
INNER JOIN categories c ON (c."ID" = lc."CategoryID")
INNER JOIN products_categories pc ON (pc."CategoryID" = c."ID")
INNER JOIN products p ON (p."ID" = pc."ProductID")
INNER JOIN wantedproducts wp ON (wp.productname = p."Name")
GROUP BY l."ID"
HAVING count(DISTINCT p."ID") = (SELECT count(*) FROM wantedproducts);