【问题标题】:Select rows that match right side of one to many reationship in MySql选择匹配MySql中一对多关系右侧的行
【发布时间】:2015-11-21 03:54:15
【问题描述】:

我有 4 张桌子。一份用于公司,一份用于产品,一份用于公司地址,一份用于公司董事。

products、director 和 address 表与 company 表是一对多的关系。

所以一家公司可以有很多产品、很多地址和很多董事。

CREATE TABLE IF NOT EXISTS `companies` (
  `company_id` int(11) NOT NULL AUTO_INCREMENT,
  `company_name` varchar(50) NOT NULL,
  PRIMARY KEY (`company_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

CREATE TABLE IF NOT EXISTS `products` (
  `product_id` int(11) NOT NULL AUTO_INCREMENT,
  `company_id` int(11) NOT NULL,
  `product` varchar(50) NOT NULL,
  PRIMARY KEY (`product_id`),
  KEY `company_id` (`company_id`),
  KEY `product` (`product`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;

CREATE TABLE IF NOT EXISTS `directors` (
  `director_id` int(11) NOT NULL AUTO_INCREMENT,
  `company_id` int(11) NOT NULL,
  `surname` varchar(100) NOT NULL,
  `dob` date NOT NULL,
  PRIMARY KEY (`director_id`),
  KEY `company_id` (`company_id`),
  KEY `surname` (`surname`),
  KEY `dob` (`dob`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;

CREATE TABLE IF NOT EXISTS `addresses` (
  `address_id` int(11) NOT NULL AUTO_INCREMENT,
  `company_id` int(1) NOT NULL,
  `postcode` varchar(10) NOT NULL,
  PRIMARY KEY (`address_id`),
  KEY `company_id` (`company_id`),
  KEY `postcode` (`postcode`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=11 ;

INSERT INTO `companies` (`company_id`, `company_name`) VALUES
(1, 'Honda'),
(2, 'Toyota');

INSERT INTO `products` (`product_id`, `company_id`, `product`) VALUES
(1, 1, 'Civic'),
(2, 1, 'Accord'),
(3, 2, 'Corolla'),
(4, 2, 'Prius'),
(5, 1, 'CRV');

INSERT INTO `directors` (`director_id`, `company_id`, `surname`, `dob`)     VALUES
(1, 1, 'Jones', '1990-09-09'),
(2, 1, 'Smith', '1980-08-08'),
(3, 2, 'Lucas', '1970-07-07'),
(4, 1, 'Kelly', '1960-06-06'),
(5, 2, 'Monty', '1950-05-05');

INSERT INTO `addresses` (`address_id`, `company_id`, `postcode`) VALUES
(6, 1, '12345'),
(7, 2, '23456'),
(8, 1, '34567'),
(9, 2, '45678'),
(10, 1, '56789');

我正在尝试编写一个有效的查询(使用 MySql / PDO)来为匹配董事(姓和 dob)和地址(邮政编码)的公司查找产品。

我只想每行列出一个匹配的产品,而不是单独列出每个董事或邮政编码。

到目前为止,我有以下查询,这似乎有效,但它很丑陋,我怀疑在速度和效率方面这是一种荒谬的方法。

SELECT product
FROM products p
LEFT JOIN companies c USING(company_id)
WHERE :lname IN ( 
    SELECT surname 
    FROM directors d 
    WHERE c.company_id = d.company_id )
AND :dob IN ( 
    SELECT dob 
    FROM directors d 
    WHERE c.company_id = d.company_id )
AND :postcode IN ( 
    SELECT postcode 
    FROM addresses a 
    WHERE c.company_id = a.company_id )

提前感谢您的帮助。

【问题讨论】:

  • 您想要“更好”工作的工作代码最好发布在codereview
  • 我会重新考虑你的底层数据结构......我不明白surnamedobpostcode 都可以如何使用。如果您有surnamedob,那么您可以通过directors 表获得@9​​87654329@...为什么您需要postcode 中的addresses 表中的company_id?这没有意义。
  • 谢谢@upful。并非所有公司都一定有地址记录,也不是所有公司都一定有董事。它们是两件独立的东西,必须在公司表上加上墨水,所以我能看到的唯一方法是通过 company_id
  • 在这种情况下,您可能会使用 OR 运算符而不是 AND... 所以 WHERE (surname = :surname AND dob = :dob) OR postcode = :pesticide ... 我认为确定哪家公司拥有这些产品很重要匹配您的搜索,而不仅仅是返回产品列表。

标签: php mysql sql join pdo


【解决方案1】:

至少,directors 上的两个子查询可以通过使用exists 运算符而不是in 重写它们来统一。为了更好地衡量,我用这个运算符重写了整个查询,尽管这不是绝对必要的:

SELECT    product
FROM      products p
LEFT JOIN companies c USING(company_id)
WHERE     EXISTS (SELECT * 
                  FROM directors d 
                  WHERE c.company_id = d.company_id AND 
                        (:lname  = d.lanme OR :dob = d.dob)) AND
           EXISTS (SELECT * 
                   FROM addresses a 
                   WHERE c.company_id = a.company_id AND :postcode = a.postcode)

【讨论】:

  • 谢谢。很有用。这是我最终使用的解决方案
【解决方案2】:

完全不确定为什么需要子查询?

SELECT p.product FROM products p
INNER JOIN companies c USING(company_id)
INNER JOIN directors d ON d.company_id = c.company_id AND d.surname = 'Jones' AND d.dob = '1990-09-09'
INNER JOIN addresses a ON a.company_id = c.company_id AND a.postcode = '12345'

或者

SELECT p.product FROM products p
INNER JOIN companies c USING(company_id)
INNER JOIN directors d USING(company_id) 
INNER JOIN addresses a USING(company_id)
WHERE d.surname = 'Jones'
AND d.dob = '1990-09-09'
AND a.postcode = '12345'

如果您对这两个查询执行 EXPLAIN,您会发现它们在内部最终是相同的。

【讨论】:

  • 感谢您的回答。这样做的问题是它为同一产品返回多行,每个债务人或邮政编码一个
  • 啊,那你可以用 DISTINCT。例如SELECT DISTINCT p.product ...
猜你喜欢
  • 2011-04-12
  • 1970-01-01
  • 2012-10-09
  • 2012-07-18
  • 1970-01-01
  • 2014-06-23
  • 2020-12-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多