【问题标题】:Getting result from SQL with min, max and condition使用最小值、最大值和条件从 SQL 获取结果
【发布时间】:2023-03-06 03:34:01
【问题描述】:

我正在尝试学习一些(高级或更复杂的)SQL。
假设我有一张汽车表,其中包含每辆车的信息。
然后我有另一张桌子,上面有正在出售的汽车,有些是新的,有些是二手的。

我希望用户能够查找汽车(例如 Honda civic 2016)并查看汽车信息。
但也希望用户查看所有本田思域 2016 款汽车的销售情况,包括该特定年份/型号的最高和最低价格,按新车和二手分类。

检索所有信息的最有效方法是什么 - 汽车信息和正在出售的信息以显示在页面上!

这些是我的桌子。

CREATE TABLE Users(
    id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(16) NOT NULL,
    last VARCHAR(16) NOT NULL,
    email VARCHAR(128) NOT NULL,
    phone CHAR(10) NOT NULL,
    joined DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE Cars(
    id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    make VARCHAR(32) NOT NULL,
    model VARCHAR(32) NOT NULL,
    year INT(4) NOT NULL,
    trim VARCHAR(16) NOT NULL
);

CREATE TABLE Market(
    id BIGINT(20) NOT NULL AUTO_INCREMENT,
    user_id BIGINT(20) NOT NULL,
    car_id BIGINT(20) NOT NULL,
    condition VARCHAR(5) NOT NULL,
    notes VARCHAR(1024) NOT NULL,
    PRIMARY KEY(id),

    CONSTRAINT cfk FOREIGN KEY (car_id) REFERENCES cars(id) ON DELETE CASCADE ON UPDATE CASCADE,
    CONSTRAINT ufk FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE
);

对我来说,它看起来是多余的,而且随着桌子变大,它注定会变慢。因此,如果有人向我展示更好的方法,我将不胜感激。

/* Get car information*/
SELECT *
FROM Cars 
WHERE make = 'Honda' 
  AND model = 'Civic' 
  AND year = '2017'   
  AND trim = 'EX'; 

/* I also would like to get the min and max price for this particular car*/
/* ?? How ?? */

/* Get (new) cars being sold and sellers */
SELECT M.*, U.* 
FROM Market M 
INNER JOIN Users ON M.user_id = U.id 
WHERE make = 'Honda' 
  AND model = 'Civic' 
  AND year = '2017' 
  AND color = 'white' 
  AND trim = 'EX' 
  AND condition = 'NEW';

/* Get (used) cars being sold and sellers */
SELECT M.*, U.* 
FROM Market M 
INNER JOIN Users ON M.user_id = U.id 
WHERE make = 'Honda' 
  AND model = 'Civic' 
  AND year = '2017' 
  AND color = 'white' 
  AND trim = 'EX' 
  AND condition = 'USED';

我最终希望使用 PHP 获得类似以下内容:

{
    car: {
        make: "Honda",
        model: "Civic",
        year: 2017,
        trim: "EX"
    },
    market: {
        new: {
            min: 'overall min',
            max: 'overall max',
            data: [{
                seller:{
                    name: "John",
                    last: "Smith",
                    phone: "xxx-xxx-xxxx",
                    email: "email@domain.com",
                },
                car: {
                    price: 15000,
                    color: "white",
                    condition: "used",
                    notes: "Some notes about the car"
                }
            }]
        },
        used: { 
            min: 'overall min',
            max: 'overall max',
            data: [{
                seller:{
                    name: "John",
                    last: "Smith",
                    phone: "xxx-xxx-xxxx",
                    email: "email@domain.com",
                },
                car: {
                    price: 15000,
                    color: "white",
                    condition: "new",
                    notes: "Some notes about the car"
                }
            }]
        }
    }
}

一旦我检索到信息,我就可以输入该格式。此外,我还必须对数据库进行分页。

基本上,我有兴趣了解亚马逊的最佳做法。一件商品从不同的供应商处以不同的价格出售。亚马逊提供有关所售商品的信息;它的状况、价格、卖家等。此外,亚马逊会为您提供最低、最高价格以及有关该商品的一般信息。最好的方法是什么?

【问题讨论】:

  • 我想你忘记了CREATE TABLE Market ... 中的price 列,明白了你的意思。数据类型 DECIMAL (10,2) 对于 price 来说可能是正常的。
  • color 也不见了..
  • 我很欣赏这些言论,但关注的焦点是性能和实现这一目标的最佳方式。剩下的事情我能应付!
  • 非常高兴您找到了您正在寻找的答案!您提供了一个极好的学习机会。这些 cmets 不适合您,而是适合稍后访问该站点以从您提供的问题和答案中受益的其他人。更正的目的是最大程度地减少混淆,并防止未来的访问者在尝试编写示例时重新发明调试轮。这里没有什么私人的!我们都只是想互相帮助。 StackOverflow 是一个很棒的资源——你是其中的一员:)感谢您的贡献!

标签: mysql


【解决方案1】:

四个查询应该没问题:

  • 查询汽车类型的一般信息
  • 另一个查询按条件获取最低/最高价格
  • 然后查询每个以获取新旧汽车的市场列表。您可以懒惰地进行这两个查询 - 首先向您的购物者显示基本汽车信息和最低/最高新/二手车价格,然后当您的购物者点击二手车号码(如亚马逊)时,仅获取二手车的市场报价.

对于最小/最大销售额,您需要进行聚合,因此GROUP BY 是您的朋友。试试这个:

SELECT `condition`, MIN(price) min_price, MAX(price) max_price
FROM Cars
JOIN Market ON (Cars.id = Market.car_id)
WHERE make = 'Honda'
  AND model = 'Civic'
  AND year = '2017'
  AND trim = 'EX'
GROUP BY `condition`; 

您的其他查询看起来不错。随着表的增长并且您希望保持快速查询,索引会有所帮助。基本规则是作为WHERE 谓词一部分的字段最好有索引。此外,表之间的任何JOIN 键通常都可以索引。在Cars(make, model, year, trim) 上尝试索引。

另外,condition 是 MySQL 5.7 中的保留字,这就是我使用反引号转义的原因。考虑改用cond,如果您只有几个条件{"new", "used"},请考虑使用ENUM 数据类型。注意MINMAX 也是保留的数据库字。

【讨论】:

  • 只是查询中的一个错字:MIN(price) max_price 应该是 MIN(price) min_price...
  • @EmileEichenberger 好眼力。修正错字。
  • 谢谢您的帮助!当桌子变得很大时怎么办?然后我应该考虑索引吗?
  • 要从给定查询中获得最佳性能,您应该定义复合索引:Cars(make, model, year, trim)Market(car_id, condition, price)。第一个索引中列的顺序对于这个查询并不重要。
  • @PaulSpiegel 如果您允许用户指定部分标准,那么 Cars 索引的顺序很重要。假设我想寻找 2010-2017 年的本田思域,任何装饰。如果它在Cars(trim, make, model, year) 上被索引,那么您将获得全表扫描。最好用最可能使用的列来引导(前缀)您的索引。如果无法将一个索引归结为所有常见用途,则多个复合索引会有所帮助。
【解决方案2】:

您可以通过两个查询来检索您需要的信息。 例如 - 您可以使用 SELECT 子句中的相关子查询在一个查询中获取新车和二手车的最低价格以及汽车信息:

SELECT c.*, 
    (SELECT MIN(m.price) FROM Market m WHERE m.car_id = c.id and m.condition = 'NEW')  as new_min_price,
    (SELECT MIN(m.price) FROM Market m WHERE m.car_id = c.id and m.condition = 'USED') as used_min_price
FROM Cars c
WHERE c.make = 'Honda' 
  AND c.model = 'Civic' 
  AND c.year = '2017'   
  AND c.trim = 'EX'

为了获得最佳性能,我将创建复合索引 Cars(make, model, year, trim)Market(car_id, condition, price)。 第一个索引中列的顺序并不重要,您可以更改它。 优化器可以将执行计划调整为您定义的顺序。 然而,第二个指数的顺序必须这样才能最有效地获得最低价格。 如果你在Market(car_id) 上有一个索引(它可能是由 FOREIGN KEY 定义创建的),你可以删除它。 可以改用新的复合索引。

请注意,我没有包括最高价格,因为我认为没有人在乎。 但是您可以通过使用MAX() 而不是MIN() 以与获得最低价格相同的方式获得它。

还有另一种使用“条件聚合”获取相同数据的方法:

SELECT c.*,
    MIN(CASE m.condition = 'NEW' THEN m.price END)  as new_min_price,
    MIN(CASE m.condition = 'USED' THEN m.price END) as used_min_price
FROM Cars c
JOIN Market m ON m.car_id = c.id
WHERE c.make = 'Honda' 
  AND c.model = 'Civic' 
  AND c.year = '2017'   
  AND c.trim = 'EX'
GROUP BY c.id

但是这个查询不能以最好的方式使用索引,因为引擎需要遍历所有的报价才能找到最低的价格。

现在要从Market 表中获取所有报价,您不需要执行两个查询(每个条件一个)。 您可以通过一个查询来获取它们,并通过应用程序语言中的condition 字段对结果进行分组。 此外,由于您已经从第一个查询中知道了 car_id,因此您可以将其用作搜索条件,因此您无需再次触摸 cars 表。 (请注意,如果没有 JOIN 与 cars 表,您的最后两个查询将无法工作,因为市场表没有列 makemodelyeartrim。)

SELECT
    m.condition
    m.id as market_id,
    m.user_id,
    u.name,
    u.last
FROM Market m
INNER JOIN Users u ON m.user_id = u.id
WHERE m.car_id = ?
ORDER BY price

请注意,如果您使用SELECT m*, u*id 字段将不明确,因为它在两个表中都定义了。 因此,您应该列出所有要获取的字段,并为不明确的列使用别名,例如 m.id as market_id

您没有提及您使用的语言 - 所以我无法准确告诉您如何按 condition 字段对结果进行分组。 例如,对于 PHP PDO,您可以使用 PDO::FETCH_GROUP 模式。 但是,如果您的语言不支持此类功能,您可以在一个简单的循环中对结果进行分组/拆分。

【讨论】:

    【解决方案3】:

    很多人可能不喜欢这样,但我已经使用 MS Access 解决了一些最复杂的 SQL 语句。首先简单地使用 MS 访问设计表和关系。然后使用查询生成器来设计查询。然后将查询生成器更改为 SQL 视图以查看 SQL 语句。要在其他数据库引擎上使用此 SQL 代码,需要进行少量更改。

    SQL Coding Kings 讨厌 MS Access,但实际上,它是开始构建任何数据库时最好的开发工具。它允许简单快速的更改,并且在单用户模式下是最快的数据库。

    如果您打算构建大量数据库并且想通过不成为 SQL 编码王来节省时间,那么我们将 MS Access 作为开发工具!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多