我在MySQL数据库中具有下一个结构:
boats
id name
-------------
1 name1
2 name2
boat_prices
id boat_id date duration price is_default
---------------------------------------------------------------
1 1 '2018-01-01' 1 100
2 1 '2018-01-01' 2 200
3 1 null null 100 1
4 2 '2018-01-02' 2 400
5 2 '2018-01-02' 4 800
6 2 null null 200 1
7 3 '2018-01-03' 5 1500
8 3 null null 300 1
船只有特定日期和时间(以天为单位)的价格。 所有船只均具有默认的“起价”价格,该价格由date = null和duration = null标识。 但是,并不是所有的船都有全天的价格。 当我搜索特定日期和持续时间的船价时,查询应返回包含该日期和持续时间的价格的所有行,并且如果船没有该日期的价格,则返回其“自”默认价格。
示例:对于日期='2018-01-01和持续时间= 1,结果应为:
boat_prices
id boat_id date duration price is_default
----------------------------------------------------------------
1 1 '2018-01-01' 1 100
6 2 null null 200 1
8 3 null null 300 1
我做这个查询示例只是为了简化,但除此之外,请考虑一下,该查询还有与其他表的其他联接。
我需要有关查询的帮助。
答案 0 :(得分:1)
我相信里克(Rick)在加入左路的方向正确,但是您可能需要两个。一种是获取符合感兴趣日期的船价,另一种是明确获取默认价格。
select
b.id,
b.name,
DefPrice.price as DefaultPrice,
Specials.price as SpecialsPrice,
COALESCE( Specials.price, DefPrice.price ) as DiscountOrDefaultPrice
from
( select @parmDate = '2018-01-01' ) sqlvars,
boats b
JOIN boat_prices DefPrice
on b.id = DefPrice.boat_id
AND DefPrice.date IS NULL
AND DefPrice.Duration IS NULL
LEFT JOIN boat_prices Specials
on b.id = Specials.boat_id
AND Specials.date <= @parmDate
AND @parmDate <= Date_Add( Specials.Date, INTERVAL (Specials.duration -1 ) DAY )
现在,如果没有特价,您总是可以通过执行COALESCE()来只返回一个有问题的价格,该价格通过DiscountOrDefaultPrice列获取默认值。
选择要与之一起运行的列的版本。这将获得所有船只,而不考虑基于持续时间的某些特殊价格。当您更改相关参数日期时,即使您更改了当前日期,该日期也会起作用。这是因为您正在针对所有可能的特殊船价及其开始到开始+持续时间结束日期范围测试相关日期。如果您有多个价格与日期重叠,则只会返回这些多个重叠的行。
我对持续时间的加法是减去1。例如,如果您的日期是2018年1月1日,并且有效期为1天,是否意味着它仅对那一天有效?或直至并包括2018年1月2日。 -1强制将资格限制为一天。因此,2018年1月1日有效期为1天的价格仅为2018年1月1日。
您在2018年1月2日收到的其他示例的持续时间为两天。对我来说,表示2天,包括01-02至01-03。实际两天。
有关日期和范围的评论中的确认
我想我的解释是错误的,然后就您的数据需求而言。您的两个有日期日期的船价记录的样本显然不够。您说您想要所有船只,而不管特殊价格记录的资格如何。因此,无论如何,您都必须从船和联接开始,以获取所有可能的“默认”定价。只需调整LEFT-JOIN组件。
话虽如此,让我们模拟更多的数据。假设您具有以下条件
Boad ID Date Duration Rate
1 2018-01-01 1 x
1 2018-01-02 4 y
2 2018-01-02 2 z
2 2018-01-04 4 a
3 2018-01-03 5 b
如果我提供日期为2018年1月1日,我应该查看哪些费率记录? 如果我提供日期2018年1月3日,会有哪些记录? 如果我提供日期2018年1月5日,什么记录?
答案 1 :(得分:1)
对于特定的日期“ 2018-01-01”和持续时间1,我将使用像这样的UNION子句:
(注意:已编辑以添加 is_default 列)
-- Get prices for particular day and duration.
(SELECT
boat_id,
date,
duration,
price,
0 AS is_default
FROM
boat_prices
WHERE
date = "2018-01-01" AND duration = 1)
UNION
-- Add defaults prices for those don't have a price on the particular day and duration
(SELECT
boat_id,
date,
duration,
price,
is_default
FROM
boat_prices
WHERE
date IS NULL
AND
duration IS NULL
AND
boat_id NOT IN (SELECT boat_id
FROM boat_prices
WHERE date ="2018-01-01" AND duration = 1))
具有存储过程解决方案的示例
DELIMITER //
CREATE PROCEDURE GetPricesByDateAndDuration(IN pDate DATE, IN pDuration INT)
BEGIN
-- Get prices for particular day and duration.
(SELECT
boat_id,
date,
duration,
price,
0 AS is_default
FROM
boat_prices
WHERE
date = pDate AND duration = pDuration)
UNION
-- Add defaults prices for those don't have a price on the particular day and duration
(SELECT
boat_id,
date,
duration,
price,
is_default
FROM
boat_prices
WHERE
date IS NULL
AND
duration IS NULL
AND
boat_id NOT IN (SELECT boat_id
FROM boat_prices
WHERE date = pDate AND duration = pDuration))
END //
DELIMITER ;
然后您可以像这样调用过程:
CALL GetPricesByDateAndDuration('2018-01-01', 1);
答案 2 :(得分:0)
而不是笨拙的输出,请考虑:
boat_id price default
-----------------------------
1 100
2 300 (default)
类似这样的东西应该生成:
SELECT boat_id,
IF(b.price IS NULL, dflt.price, b.price) AS price,
IF(b.price IS NULL, '(default)', '') AS default
FROM boat_prices AS dflt
LEFT JOIN boat_prices AS b USING(boat_id)
WHERE dflt.date IS NULL
AND dflt.duration IS NULL
AND '2018-01-01' >= b.date
AND '2018-01-01' < b.date + INTERVAL b.duration DAY
GROUP BY boat_id