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

I set datefirst 1 but i can't how to improve this. I'm sorry for less information. I'm using SQL Server. I uploaded the .img to exemplify.

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

I want to show somwthing like this

PORT            COLLECT             DEPARTURE

MANAUS      07/12 TO 12/12     15/12/2018(ODD DAY)
BRKZ
  • 47
  • 6
  • 2
    Please provide sample data and desired results. The description doesn't make much sense without sample data. Also tag with the database you are using. – Gordon Linoff Dec 18 '18 at 15:54
  • Can you elaborate a bit more about the results and data types? Also tag which DBMS you are using (I suspect SQL Server). What do you mean with "the departure date should be in wednesday to monday"? – EzLo Dec 18 '18 at 15:57
  • @BRKZ you can use the edit button to add more info on your question. – EzLo Dec 18 '18 at 16:17
  • I'm sorry for less information. I edited and add some infos. Sorry about my english. I hope you can understand. – BRKZ Dec 18 '18 at 16:27
  • @BRKZ whats your requirement?? – Nikhil S Dec 18 '18 at 16:32
  • How do you define *odd*, based on day of week or day of month? *Monday/Wednesday/Friday* or *1/3/5/7/..* – dnoeth Dec 18 '18 at 16:35
  • based on day of week. I use DATEPART( Deaparture_Date) and i get the day of the month. But I can't apply this on `case` – BRKZ Dec 18 '18 at 16:37

2 Answers2

3

I highly recommend using a Calendar Table for this. A calendar table holds date values with additional information, so it's easier to find particular days (like business days or weekdays, as for this example).

The following solution uses a calendar table and 2 CROSS APPLY operators to get the previous collection dates.

This is how you can create a calendar table (recursive 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)

The table is like the following:

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

We will use this to find the closest Wednesday and Monday just before a particular Departure date. We find this using a CROSS APPLY having the DepartureDate as a higher limit, then searching for the particular weekday (1 for monday, 3 for wednesday). Then use TOP 1 with ORDER BY Date DESC to get the highest Monday/Wednesday just before that departure date.

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

Finding the previous Friday from a Wednesday is as simple as moving backwards 5 days, the same goes from Monday to Wednesday.

Results:

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

This was a good SQL exercise.

EzLo
  • 13,780
  • 10
  • 33
  • 38
  • 2
    Any time you find yourself doing much more than simple date calculations, you should probably consider a Calendar Table. Actually, any time you have a database, you should probably add a Calendar Table and then modify it as needed. Both a Calendar Table and a Numbers Table sound like huge wastes, but they can easily and significantly increase performance. This should be a fundamental topic for any database design or usage. I wish that any time someone enters a question about SQL (any flavor), a "Have You Tried A Calendar Table?" hint popped up. – Shawn Dec 18 '18 at 19:44
0

Really thanks @Ezlo, thats was excellent. It's a good SQL exercise. I think this will suit me well on my job. Sorry to look boring, I've some questions:

Edit:. I noticed that the dates of V(Departure) is fix. I want to make the dates dinamic. Because when I set the days on

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

The dates of collect as fix for all Ports. Sorry if i boring. You guys help a lot.

BRKZ
  • 47
  • 6
  • Table `V` from my example is `SampleData` which are hard-coded values I used as example. Simply remove the CTE called `SampleData` and replace `FROM SampleData AS V` with your original table that holds the departure values. The cross applies don't need changing (don't need to reference `V` table again inside the cross apply, just it's link through `C.Date < V.Departure`, this is called correlated subquery). – EzLo Dec 19 '18 at 07:26
  • Thank you very much @EzLo. Works well! I just have a little problem. When departure date is 25/12/2018 01:00:00 my collect date transcend the departure date. COLLECT DEPARTURE `2018-12-24 TO 2018-12-29 - 25/12/2018 01:00:00` But the cross aplly it's works from another dates. `CROSS APPLY (SELECT TOP 1 C.DATE FROM #CALENDARTABLE C WHERE C.DATE < CONVERT(DATE,ESCALA_ORIGEM.DATA_PREV_SAIDA) AND C.WEEKDAY = 7 ORDER BY C.DATE DESC) AS M` – BRKZ Dec 19 '18 at 12:37
  • on the query I posted there is the example of 2018-12-25 that doesn't have collect higher than this date (2018-12-14 TO 2018-12-19). Maybe you did some editions? Try converting the datetime values to date before comparing against the calendar table. Make sure that the correct column is `ESCALA_ORIGEM.DATA_PREV_SAIDA`. Try hard-coding the date and executing the select, it shouldn't bring any date higher than 25. – EzLo Dec 19 '18 at 12:47
  • Thanks for your time @EzLo. Helps a lot. I'm trying to fix the date 2018-12-25. Until the moment, not successful. If I fix, I'll report here. – BRKZ Dec 19 '18 at 15:21
  • I fix palliatively. I made another case just for DAY (25). Probably in 2019 I'll need to update the case. – BRKZ Dec 19 '18 at 18:08