具有两个表和多个数据和价格范围的复杂查询

发布于 2024-09-01 10:51:00 字数 770 浏览 7 评论 0原文

假设我有这些表:

[ properties ]
 id (INT, PK)
 name (VARCHAR)

[ properties_prices ]
 id (INT, PK)
 property_id (INT, FK)
 date_begin (DATE)
 date_end (DATE)
 price_per_day (DECIMAL)
 price_per_week (DECIMAL)
 price_per_month (DECIMAL)

我的访问者运行如下搜索: 列出 5 月 1 日至 12 月 31 日期间每天价格(price_per_day 字段)在 10 到 100 之间的前 10 个(分页)属性

我知道这是一个巨大的查询,我需要对结果进行分页,所以我必须完成所有计算并仅在一个查询中登录......这就是我在这里的原因! :)

有关问题的疑问

如果存在差距,这是否是可接受的属性?

没有间隙。所有可能的日期都在数据库中。

如果在某些超级时期价格在 10 到 100 之间,但在其他时期不是,您想购买该房产吗?

在完美的世界中,不......我们需要考虑所有变化/时期来计算该时期内该类型价格的“总和”。

另外,“前10个”是什么?它们是如何订购的?最低价优先?但价格可能不止一种。

这只是一个分页示例,每页有 10 个结果...可以通过全文搜索进行排序,我将添加关键字和这些内容...正如我所说,这是一个相当大的查询。

Let's suppose that I have these tables:

[ properties ]
 id (INT, PK)
 name (VARCHAR)

[ properties_prices ]
 id (INT, PK)
 property_id (INT, FK)
 date_begin (DATE)
 date_end (DATE)
 price_per_day (DECIMAL)
 price_per_week (DECIMAL)
 price_per_month (DECIMAL)

And my visitor runs a search like: List the first 10 (pagination) properties where the price per day (price_per_day field) is between 10 and 100 on the period for 1st may until 31 december

I know thats a huge query, and I need to paginate the results, so I must do all the calculation and login in only one query... that's why i'm here! :)

Questions about the problem

If there are gaps, would that be an acceptable property?

There're no gaps. All the possible dates are in the database.

If the price is between 10 and 100 in some sup-periods, but not in others, do you want to get that property?

In the perfect world, no... We'll need to calculate the "sum" of that type of price in that period considering all the variations/periods.

Also, what are the "first 10"? How are they ordered? Lowest price first? But there could be more than one price.

This is just an example of pagination with 10 results per page... Can be ordered by the FULLTEXT search that I'll add with keywords and these things... As I said, it's a pretty big query.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

泼猴你往哪里跑 2024-09-08 10:51:00

这与 @mdma 给出的答案类似,但我在价格范围的连接子句中使用条件,而不是 HAVING 技巧。

SELECT p.id, MAX(p.name), 
  MIN(v.price_per_day) AS price_low,
  MAX(v.price_per_day) AS price_high
FROM properties p
JOIN properties_prices v ON p.id = v.property_id
  AND v.price_per_day BETWEEN 10 AND 100  
  AND v.date_begin < '2010-12-31' AND v.date_end > '2010-05-01'
GROUP BY p.id
ORDER BY ...
LIMIT 10;

我还建议创建覆盖索引:

CREATE INDEX prices_covering ON properties_prices
  (property_id, price_per_day, date_begin, date_end);

这允许您的查询尽可能最佳地运行,因为它可以直接从索引读取值。它根本不需要从表中读取数据行。

+----+-------------+-------+-------+-----------------+-----------------+---------+-----------+------+--------------------------+
| id | select_type | table | type  | possible_keys   | key             | key_len | ref       | rows | Extra                    |
+----+-------------+-------+-------+-----------------+-----------------+---------+-----------+------+--------------------------+
|  1 | SIMPLE      | p     | index | PRIMARY         | PRIMARY         | 4       | NULL      |    1 |                          |
|  1 | SIMPLE      | v     | ref   | prices_covering | prices_covering | 4       | test.p.id |    6 | Using where; Using index |
+----+-------------+-------+-------+-----------------+-----------------+---------+-----------+------+--------------------------+

This is similar to the answer given by @mdma, but I use a condition in the join clause for the price range, instead of the HAVING trick.

SELECT p.id, MAX(p.name), 
  MIN(v.price_per_day) AS price_low,
  MAX(v.price_per_day) AS price_high
FROM properties p
JOIN properties_prices v ON p.id = v.property_id
  AND v.price_per_day BETWEEN 10 AND 100  
  AND v.date_begin < '2010-12-31' AND v.date_end > '2010-05-01'
GROUP BY p.id
ORDER BY ...
LIMIT 10;

I would also recommend creating a covering index:

CREATE INDEX prices_covering ON properties_prices
  (property_id, price_per_day, date_begin, date_end);

This allows your query to run as optimally as possible, because it can read the values directly from the index. It won't have to read the rows of data from the table at all.

+----+-------------+-------+-------+-----------------+-----------------+---------+-----------+------+--------------------------+
| id | select_type | table | type  | possible_keys   | key             | key_len | ref       | rows | Extra                    |
+----+-------------+-------+-------+-----------------+-----------------+---------+-----------+------+--------------------------+
|  1 | SIMPLE      | p     | index | PRIMARY         | PRIMARY         | 4       | NULL      |    1 |                          |
|  1 | SIMPLE      | v     | ref   | prices_covering | prices_covering | 4       | test.p.id |    6 | Using where; Using index |
+----+-------------+-------+-------+-----------------+-----------------+---------+-----------+------+--------------------------+
伤痕我心 2024-09-08 10:51:00

你告诉我们的不够精确。根据您的数据结构和您的问题,我假设:

  • 该时期内房产的价格可能会发生变化,并且每个子时期都会有一个properties_price条目,
  • 子时期中不应该有重叠,但数据结构不会保证
  • 子期间可以有间隙,

但仍然存在问题:

  • 如果有间隙,这是否是可接受的属性?
  • 如果在某些超级时期价格在 10 到 100 之间,但在其他时期不是,您想购买该房产吗?
  • 另外,“前10个”是什么?它们是如何订购的?最低价优先?但价格可能不止一种。

根据答案,可能没有单个查询可以解决问题。但是,如果您接受差距,则可能会返回您想要的结果:

SELECT *
FROM properties AS p
WHERE EXISTS          -- property is available in the price range
     (SELECT * FROM properties_prices AS pp1 
      WHERE p.id = pp1.property_id AND
            pp1.price_per_day between 10 and 100 AND
            (pp1.date_begin <= "2010-12-31" OR pp1.date_end >= "2010-05-01")) AND
      NOT EXISTS      -- property is in the price range in all sup-periods, but there might be gaps
     (SELECT * FROM properties_prices AS pp2 
      WHERE p.id = pp2.property_id AND
            pp2.price_per_day not between 10 and 100 AND
            (pp2.date_begin <= "2010-12-31" OR pp2.date_end >= "2010-05-01"))
ORDER BY name  --- ???
LIMIT 10  

该查询不会为您提供价格或其他详细信息。这需要进行额外的查询。但也许我的假设都是错误的。

What you tell us is not precise enough. From your data structure and your question I assume:

  • the price of a property can change in that period, and there would be a properties_price entry for each sub-period
  • there should be no overlaps in the sub-periods, but the data structure does not guarantee that
  • there can be gaps in the sub-periods

But there are still questions:

  • If there are gaps, would that be an acceptable property?
  • If the price is between 10 and 100 in some sup-periods, but not in others, do you want to get that property?
  • Also, what are the "first 10"? How are they ordered? Lowest price first? But there could be more than one price.

Depending on the answers, there might be no single query doing the trick. But if you accept the gaps, that could return what you want:

SELECT *
FROM properties AS p
WHERE EXISTS          -- property is available in the price range
     (SELECT * FROM properties_prices AS pp1 
      WHERE p.id = pp1.property_id AND
            pp1.price_per_day between 10 and 100 AND
            (pp1.date_begin <= "2010-12-31" OR pp1.date_end >= "2010-05-01")) AND
      NOT EXISTS      -- property is in the price range in all sup-periods, but there might be gaps
     (SELECT * FROM properties_prices AS pp2 
      WHERE p.id = pp2.property_id AND
            pp2.price_per_day not between 10 and 100 AND
            (pp2.date_begin <= "2010-12-31" OR pp2.date_end >= "2010-05-01"))
ORDER BY name  --- ???
LIMIT 10  

That query doesn't give you the prices or other details. That would need to go in an extra query. But perhaps my assumptions are all wrong anyway.

掩耳倾听 2024-09-08 10:51:00

这也可以作为 GROUP BY 来完成,我认为这会非常有效,并且我们得到一些聚合作为包的一部分:(

SELECT 
   prperty_id, MIN(price_per_day), MAX(price_per_day)
FROM 
   properties_prices 
WHERE 
   date_begin <= "2010-12-31" AND date_end >= "2010-05-01"
GROUP BY 
   property_id
HAVING MIN(IF( (price_per_day BETWEEN 10 AND 100), 1, 0))=1
ORDER BY ...
LIMIT 10

我手头没有 MySQL,所以我没有测试过。我不确定MIN(IF ...) 但使用 CASE 的模型在 SQLServer 上运行。)

This can also be done as a GROUP BY, which I think will be quite efficient, and we get some aggregates as part of the package:

SELECT 
   prperty_id, MIN(price_per_day), MAX(price_per_day)
FROM 
   properties_prices 
WHERE 
   date_begin <= "2010-12-31" AND date_end >= "2010-05-01"
GROUP BY 
   property_id
HAVING MIN(IF( (price_per_day BETWEEN 10 AND 100), 1, 0))=1
ORDER BY ...
LIMIT 10

(I don't have MySQL to hand so I haven't tested. I was unsure about the MIN(IF ...) but a mock-up using a CASE worked on SQLServer.)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文