【问题标题】:DATEPART fix daysDATEPART 修复天数
【发布时间】:2018-12-18 15:53:01
【问题描述】:
CASE WHEN DAY % 2 = 0 AND POL = 'SUUA'
THEN CONVERT(VARCHAR(15),DATEPART(DAY,5)) + ' TO ' + CONVERT(VARCHAR(15),DATEPART(DAY,3),103)

我将 datefirst 设置为 1,但我无法改进这一点。 我很抱歉信息少。我正在使用 SQL Server。 我上传了 .img 来举例说明。

CASE WHEN DAY % 2 = 0 AND POL = 'SUUA' THEN CONVERT(VARCHAR(15),DATEPART(DAY,5)) + ' TO ' + CONVERT(VARCHAR(15),DATEPART(DAY,3),103)

我想展示这样的东西

PORT            COLLECT             DEPARTURE

MANAUS      07/12 TO 12/12     15/12/2018(ODD DAY)

【问题讨论】:

  • 请提供样本数据和期望的结果。如果没有样本数据,描述就没有多大意义。同时标记您正在使用的数据库。
  • 您能详细说明一下结果和数据类型吗?还要标记您正在使用的 DBMS(我怀疑是 SQL Server)。 “出发日期应该是星期三到星期一”是什么意思?
  • @BRKZ 您可以使用编辑按钮添加有关您的问题的更多信息。
  • 很抱歉信息较少。我编辑并添加了一些信息。对不起我的英语。希望你能理解。
  • @BRKZ 你有什么要求??

标签: sql sql-server tsql datepart


【解决方案1】:

我强烈建议为此使用日历表。日历表包含带有附加信息的日期值,因此更容易找到特定日期(如工作日或工作日,如本例所示)。

以下解决方案使用日历表和 2 个CROSS APPLY 运算符来获取以前的收集日期。

这是创建日历表的方法(递归 CTE):

SET DATEFIRST 1 -- 1: Monday, 7: Sunday

-- Create a Calendar Table
IF OBJECT_ID('tempdb..#CalendarTable') IS NOT NULL
    DROP TABLE #CalendarTable

;WITH CalendarTable AS
(
    SELECT
        Date = CONVERT(DATE, '2016-01-01'),
        Weekday = DATEPART(WEEKDAY, '2016-01-01')

    UNION ALL

    SELECT
        Date = DATEADD(DAY, 1, C.Date),
        Weekday = DATEPART(WEEKDAY, DATEADD(DAY, 1, C.Date))
    FROM
        CalendarTable AS C
    WHERE
        C.Date <= '2020-01-01'
)
SELECT
    C.Date,
    C.Weekday
INTO
    #CalendarTable
FROM
    CalendarTable AS C
OPTION
    (MAXRECURSION 0)

表格如下:

SELECT * FROM #CalendarTable ORDER BY Date DESC

Date        Weekday
2020-01-02  4
2020-01-01  3
2019-12-31  2
2019-12-30  1
2019-12-29  7
2019-12-28  6
2019-12-27  5
2019-12-26  4
2019-12-25  3
2019-12-24  2
2019-12-23  1
2019-12-22  7
2019-12-21  6
2019-12-20  5
2019-12-19  4
2019-12-18  3
2019-12-17  2
2019-12-16  1
2019-12-15  7
2019-12-14  6
2019-12-13  5
2019-12-12  4
2019-12-11  3

我们将使用它来查找特定出发日期之前最接近的星期三和星期一。我们使用CROSS APPLYDepartureDate 作为上限,然后搜索特定的工作日(1 代表星期一,3 代表星期三)来找到这个。然后使用TOP 1ORDER BY Date DESC 获得出发日期之前的最高周一/周三。

-- Build your Collect periods
;WITH SampleData AS
(
    SELECT
        V.Departure
    FROM
        (VALUES
            ('2018-12-01'),
            ('2018-12-09'),
            ('2018-12-25'),
            ('2018-12-29'),
            ('2019-01-02'),
            ('2019-01-07'),
            ('2019-01-10')) AS V(Departure)
)
SELECT
    V.Departure,

    -- Friday to Wednesday
    ClosestWednesdayBeforeDeparture = W.Date,
    PreviousFridayOfThatWednesday = DATEADD(DAY, -5, W.Date),

    -- Wednesday to Monday
    ClosestMondayBeforeDeparture = M.Date,
    PreviousWednesdayOfThatMonday = DATEADD(DAY, -5, M.Date),

    -- Check for odd/even
    IsOdd = CASE WHEN DATEPART(DAY, V.Departure) % 2 = 1 THEN 1 ELSE 0 END,

    -- Use previous expressions to build your collect periods
    Collect = CASE
        WHEN 
            DATEPART(DAY, V.Departure) % 2 = 1 -- IsOdd
        THEN
            CONVERT(VARCHAR(100), DATEADD(DAY, -5, W.Date), 120) -- PreviousFridayOfThatWednesday
            + ' TO '
            + CONVERT(VARCHAR(100), W.Date, 120) -- ClosestWednesdayBeforeDeparture

        ELSE -- IsEven
            CONVERT(VARCHAR(100), DATEADD(DAY, -5, M.Date), 120) -- PreviousWednesdayOfThatMonday
            + ' TO '
            + CONVERT(VARCHAR(100), M.Date, 120) -- ClosestMondayBeforeDeparture
        END
FROM
    SampleData AS V
    CROSS APPLY (
        SELECT TOP 1
            C.Date
        FROM
            #CalendarTable AS C
        WHERE
            C.Date < V.Departure AND
            C.Weekday = 3 -- 3: Wednesday
        ORDER BY
            C.Date DESC) AS W
    CROSS APPLY (
        SELECT TOP 1
            C.Date
        FROM
            #CalendarTable AS C
        WHERE
            C.Date < V.Departure AND
            C.Weekday = 1 -- 1: Monday
        ORDER BY
            C.Date DESC) AS M
ORDER BY
    V.Departure

从星期三查找上一个星期五就像向后移动 5 天一样简单,从星期一到星期三也是如此。

结果:

Departure   IsOdd   Collect                     ClosestWednesdayBeforeDeparture     PreviousFridayOfThatWednesday   ClosestMondayBeforeDeparture    PreviousWednesdayOfThatMonday
2018-12-01  1       2018-11-23 TO 2018-11-28    2018-11-28                          2018-11-23                      2018-11-26                      2018-11-21
2018-12-09  1       2018-11-30 TO 2018-12-05    2018-12-05                          2018-11-30                      2018-12-03                      2018-11-28
2018-12-25  1       2018-12-14 TO 2018-12-19    2018-12-19                          2018-12-14                      2018-12-24                      2018-12-19
2018-12-29  1       2018-12-21 TO 2018-12-26    2018-12-26                          2018-12-21                      2018-12-24                      2018-12-19
2019-01-02  0       2018-12-26 TO 2018-12-31    2018-12-26                          2018-12-21                      2018-12-31                      2018-12-26
2019-01-07  1       2018-12-28 TO 2019-01-02    2019-01-02                          2018-12-28                      2018-12-31                      2018-12-26
2019-01-10  0       2019-01-02 TO 2019-01-07    2019-01-09                          2019-01-04                      2019-01-07                      2019-01-02

这是一个很好的 SQL 练习。

【讨论】:

  • 任何时候您发现自己所做的不仅仅是简单的日期计算,您可能应该考虑使用日历表。实际上,任何时候你有一个数据库,你可能应该添加一个日历表,然后根据需要对其进行修改。日历表和数字表听起来都是巨大的浪费,但它们可以轻松且显着提高性能。这应该是任何数据库设计或使用的基本主题。我希望任何时候有人输入关于 SQL(任何风格)的问题,“你试过日历表吗?”提示弹出。
【解决方案2】:

真的很感谢@Ezlo,那太棒了。这是一个很好的 SQL 练习。我认为这很适合我的工作。抱歉看起来很无聊,我有一些问题:

编辑:。我注意到 V(Departure) 的日期是固定的。我想让日期动态。因为当我设置日期时

CROSS APPLY(SELECT TOP 1 
            C.DATE
        FROM 
            #CALENDARTABLE C, SAMPLEDATA V
        WHERE
            C.DATE < V.DEPARTURE AND
            C.WEEKDAY = 1 
        ORDER BY
            C.DATE DESC) AS W

CROSS APPLY (SELECT TOP 1
            C.DATE
        FROM
            #CALENDARTABLE C,SAMPLEDATA V
        WHERE
            C.DATE < V.DEPARTURE AND
            C.WEEKDAY = 7 
        ORDER BY
            C.DATE DESC) AS M

所有港口的收货日期。 对不起,如果我很无聊。你们帮了大忙。

【讨论】:

  • 我的示例中的表VSampleData,这是我用作示例的硬编码值。只需删除名为SampleData 的CTE 并将FROM SampleData AS V 替换为包含出发值的原始表。交叉应用不需要更改(交叉应用内部不需要再次引用V表,只是通过C.Date &lt; V.Departure的链接,这称为相关子查询)。
  • 非常感谢@EzLo。效果很好!我只是有一个小问题。当出发日期为 25/12/2018 01:00:00 时,我的取件日期将超过出发日期。收集出发2018-12-24 TO 2018-12-29 - 25/12/2018 01:00:00 但十字架适用于其他日期。 CROSS APPLY (SELECT TOP 1 C.DATE FROM #CALENDARTABLE C WHERE C.DATE &lt; CONVERT(DATE,ESCALA_ORIGEM.DATA_PREV_SAIDA) AND C.WEEKDAY = 7 ORDER BY C.DATE DESC) AS M
  • 在我发布的查询中,有 2018-12-25 的示例没有收集高于此日期(2018-12-14 TO 2018-12-19)。也许你做了一些版本?在与日历表进行比较之前,请尝试将日期时间值转换为日期。确保正确的列是ESCALA_ORIGEM.DATA_PREV_SAIDA。尝试硬编码日期并执行选择,它不应该带来任何高于 25 的日期。
  • 感谢您的时间@EzLo。有很大帮助。我正在尝试修复日期 2018-12-25。直到此刻,还没有成功。如果我解决了,我会在这里报告。
  • 我姑息修复。我为DAY(25)做了另一个案例。可能在 2019 年我需要更新案例。
猜你喜欢
  • 2011-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-06
  • 1970-01-01
  • 1970-01-01
  • 2020-06-05
相关资源
最近更新 更多