具有条件结果的复杂mysql查询

时间:2018-09-11 11:28:22

标签: mysql

我在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

我做这个查询示例只是为了简化,但除此之外,请考虑一下,该查询还有与其他表的其他联接。

我需要有关查询的帮助。

3 个答案:

答案 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