如何检测表中是否存在一组两个值?

时间:2015-02-19 12:23:14

标签: mysql sql

我有一张桌子ProductCustomer 其中包含两个字段:ProductIDCustomerID
它代表哪个客户购买了哪种产品

让我们假设它有以下数据:

+-----------+-------------+
| ProductID |  CustomerID |
+-----------+-------------+
|     17    |     105     |
|      8    |     201     |
|     17    |     123     |
|      9    |      36     |
|      5    |     950     |
|     14    |     204     |
|      8    |     116     |
|     39    |     105     |
+-----------+-------------+ 

我想通过一个查询检测具有ID 1781239的产品是否由ID为105的客户购买。
即查询将列表(17, 8, 12, 39)和值105作为参数,结果应为:

+-----------+----------+
| ProductID | isBought |
+-----------+----------+
|     17    |     1    | ---> Bought twice, once by CutomerId 105
|      8    |     0    | ---> Bought twice, none of them by CutomerId 105
|     12    |     0    | ---> Was not bought ever by any customer
|     39    |     1    | ---> Bought once, which was by CutomerId 105
+-----------+----------+ 

哪个查询可以帮我推断出这个结果集?

如果这很重要,我正在使用MySQL。


更新:产品表存在于具有其他权限的另一个模式中。我无法在当前查询中使用它。

6 个答案:

答案 0 :(得分:1)

更新添加了一个获得所需结果的查询,无需在客户端进行任何额外编码。

原始答案

子查询和GROUP BY对于这么简单的任务来说太过分了。

此查询:

SELECT DISTINCT ProductID
FROM Product_Customer
WHERE ProductID IN (17, 8, 12, 39)
  AND CustomerID = 105

会向您返回客户购买的ProductID列表 - 您所需结果的1列中包含isBought的行。

在客户端语言上使用几行代码来填充其余部分:创建一个包含所有输入产品的列表并初始化元素' isBought属性为零。运行查询。为返回的产品设置值1是查询。

例如,使用PHP它将如下所示:

// Input data
$productIDs = array(17, 8, 12, 39);
$customerID = 105;

// Prepare the list of products indexed by ProductID
// None of them was bought by the customer yet (this is all the information
// we have at this point; it will be updated below)
$list = array_fill_keys($productIDs, 0);

// Run the query
// Assume we already have a PDO connection in $db
// In production code you should protect the query against injection
// using either quoting or, better, prepared statements.
$result = $db->query(
    'SELECT ProductID '.
    'FROM Product_Customer '.
    'WHERE ProductID IN ('.implode($productIDs).') '.
        'AND CustomerID = '.$customerID
);

// Get the values
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
    $prodID = intval($row['ProductID']);
    // Mark the product as bought
    $list[$prodID] = 1;
}

// Dump $list to check the outcome
print_r($list);

更新

可以通过JOIN - 表ProductsFK`ProductID来自的表)直接从数据库中检索所需的结果:

SELECT DISTINCT p.ProductID, IF(pc.ProductID IS NULL, 0, 1) AS isBought
FROM Product_Customer pc
  RIGHT JOIN Products p ON pc.ProductID = p.ProductID AND pc.CustomerID = 105
WHERE p.ProductID IN (17, 8, 12, 39)

如果表Products不可用(OP在问题中添加了这个额外条件),则需要以某种方式无处生成包含所有所需ProductID的结果集。

一种可能性是SELECT单独查询中的每个值UNION

SELECT DISTINCT p.ProductID, IF(pc.ProductID IS NULL, 0, 1) AS isBought
FROM Product_Customer pc
  RIGHT JOIN (
    SELECT 17 AS ProductID
    UNION
    SELECT 8 AS ProductID
    UNION
    SELECT 12 AS ProductID
    UNION
    SELECT 39 AS ProductID
  ) p ON pc.ProductID = p.ProductID AND pc.CustomerID = 105

我们不再需要WHERE条件,因为我们即时生成的假Products表(使用UNION)仅包含ProductID的所需值{1}}并没有别的。)

它并不比原始查询慢(请参阅第一个查询,即仅返回Product_Customer表中存在的产品的查询),我认为它比JOIN的查询更快是真正的Products表。


this answer揭示了另一种解决方案(更深奥但更有效)。

答案 1 :(得分:0)

您可以尝试以下操作:(Working SQLFiddle

SELECT 
    ProductID, 
    (SELECT COUNT(*) FROM ProductCustomer AS p2 WHERE CustomerID=105 AND p1.ProductID=p2.ProductID) AS isBought 
FROM Product AS p1 
WHERE ProductID IN (17, 8, 12, 39)

答案 2 :(得分:0)

考虑以下内容......

DROP TABLE IF EXISTS customer;
DROP TABLE IF EXISTS product;
DROP TABLE IF EXISTS product_customer;

CREATE TABLE product
(product_id INT NOT NULL PRIMARY KEY);

CREATE TABLE customer
(customer_id INT NOT NULL PRIMARY KEY);

CREATE TABLE product_customer
(product_id INT NOT NULL
,customer_id INT NOT NULL
,PRIMARY KEY(product_id,customer_id)
);

INSERT INTO product VALUES
(5),(8),(9),(14),(12),(17),(39);

INSERT INTO customer VALUES
(36),
(105),
(201),
(123),
(950),
(204),
(116);

INSERT INTO product_customer VALUES
(17    ,105),
(8     ,201),
(17    ,123),
(9     , 36),
(5     ,950),
(14    ,204),
(8     ,116),
(39    ,105);

SELECT p.*
     , MAX(CASE WHEN c.customer_id = 105 THEN 1 ELSE 0 END) isbought 
  FROM product p 
  LEFT 
  JOIN product_customer pc 
    ON pc.product_id = p.product_id 
  LEFT 
  JOIN customer c 
    ON c.customer_id = pc.customer_id 
 WHERE p.product_id IN(17,8,12,39)
 GROUP 
    BY p.product_id;
+------------+----------+
| product_id | isbought |
+------------+----------+
|          8 |        0 |
|         12 |        0 |
|         17 |        1 |
|         39 |        1 |
+------------+----------+

答案 3 :(得分:0)

由于并非所有产品都存在于表ProductCustomer中,因此您必须从表Product中进行选择,以获取每个请求产品的结果记录。

如果您现在想知道表ProductCustomer中是否存在记录 ,您应该明显使用EXISTS。

select 
  productid, 
  exists 
  (
    select * from productcustomer pc 
    where pc.productid = p.productid and pc.customerid = 105
  ) as isbought
from product p
where productid in (17, 8, 12, 39);

更新:由于无法访问产品表,因此必须生成要显示的记录。例如:

select 
  productid, 
  exists 
  (
    select * from productcustomer pc 
    where pc.productid = p.productid and pc.customerid = 105
  ) as isbought
from 
(
  select 17 as productid union all 
  select 8               union all 
  select 12              union all  
  select 39
) p;

答案 4 :(得分:0)

以下说明产品#12在表中不存在,但假设您的表已经有一些行......

  SET @a:=0.5;
  SELECT @b:=MAKE_SET(@a:=@a*2, 17,8,12,39) ProductID,
     IFNULL((SELECT 1 FROM mytable2
     WHERE ProductID=@b AND CustomerID=105 LIMIT 1), 0) isBought
     FROM mytable2 m1
     LIMIT 4;

 +-----------+----------+
 | ProductID | isBought |
 +-----------+----------+
 | 17        |        1 |
 | 8         |        0 |
 | 12        |        0 |
 | 39        |        1 |
 +-----------+----------+

当然,如果你有产品表,你只需要做:

 SELECT p.ProductID, IFNULL((SELECT 1 FROM mytable2
    WHERE ProductID=p.ProductID AND CustomerID=105 LIMIT 1), 0) isBought
 FROM Products p
 WHERE p.ProductID IN(17,8,12,39);

答案 5 :(得分:-1)

GROUP BY,使用SUM计算大小写

select ProductID, sum(case when CustomerID = 105 then 1 else 0 end)
from tablename
where ProductID in (17, 8, 12, 39)
group by ProductID