How to create a query to get invoices between dates (help needed) - sql

I need to create a query in SQL to get some invoices depending on the current date. I'm an intern in the company, so I only need to create the logic, not necessarily the query. Can anyone help me to translate this conditions into SQL (Snowflake)?
If Current Date (today's date) is between day 14 and 27 of the month >>> Get
invoices from day 14 of the current month
If Current Date (today's date) is between day 28 and 31 of the month >>> Get
invoices from day 28 of the current month
If Current Date (today's date) is between day 1 and 13 of the month >>> Get invoices
from day 28 of the last month
Thanks in advance!

I think your simplest, clearest option is a case statement.
You can put this into your select and work with it from there, or put it directly into a where clause. whatever's more appropriate for your query.
WHERE invoice_date::DATE = (CASE WHEN DAY(current_date) between 14 and 27 then DATE_FROM_PARTS(YEAR(current_date), MONTH(current_date), 14)
WHEN DAY(current_date) between 28 and 31 then DATE_FROM_PARTS(YEAR(current_date), MONTH(current_date), 28)
WHEN DAY(current_date) between 1 and 13 then DATEADD('month', -1, DATE_FROM_PARTS(YEAR(current_date), MONTH(current_date), 28))
END)

select
current_date()+1 your_date
,dateadd(day,27-(sign(FLOOR(day(your_date)/14,0))*14)
,dateadd(month,sign(FLOOR(day(your_date)/14,0))-1
,date_trunc(month,your_date))) your_result
Revised solution.
Removing inequalities and reducing the problem down to multiples of 14 with month adjustments instead of multiple case statements (expensive to run at scale).

Related

Custom month numbers that take last 30 days instead of Number of month (SQL Server)

I am trying to create a lag function to return current month and last month streams for an artist.
Instead of returning streams for Feb vs Jan, I wan the function to use the last 30 days as a period for current month, and the previous 30 days as the previous month.
The query that I am currently using is this:
SELECT
DATEPART(month, date) AS month,
artist,
SUM([Streams]) AS streams,
LAG(SUM([Streams])) OVER (PARTITION BY artist ORDER BY DATEPART(month, date)) AS previous_month_streams
FROM combined_artist
WHERE date > DATEADD(m, -2, DATEADD(DAY, 2 - DATEPART(WEEKDAY, GETDATE()-7), CAST(GETDATE()-7 AS DATE)))
GROUP BY DATEPART(month, date), artist;
While this works, it is not giving me the data I need. This is returning the sum of streams for February vs the Streams for the month of January. February seems very low because we only have one week worth of data in February.
My goal is to get the last 30 days from the max date in the table using a lag function. So if the max date is Feb. 7 2023, I want the current month to include data from Jan. 7 2023 - Feb. 7 2023, and the previous month to include data from Dec. 7 2022 - Jan. 7 2023. I am thinking to create a custom month date part that will start from the max date and give a month number to the last 30 days . (2 for Jan 7 - Feb 7, 1 for Dec 7 - Jan-7...) I am not sure how to go about this. This is in SQL Server and I am looking to use the lag function for performance reasons.
I think you could probably use something like datediff(d, date_you_care_about, max_date)/30 in your group by and partition by clauses.
The basic idea is that integer division rounds down, so if the difference between the dates is < 30, dividing it by 30 is 0. If the difference is >=30 but less than 60, dividing it by 30 is 1. And so forth.
You can see a proof of concept in this Fiddle.

Find and sort all the data(dates) after a certain date by month/day and not year in SQLite

I wanna order the data in an sqlite3 database by date. (Day and Month to be precise)
I have a table,
the data in the table are in the format YYYY-MM-DD
2003-02-20, 2005-07-16, 2008-11-18, 1998-01-02, 1996-08-27
Here, I wanna find all the data after a certain date(Current date- 'now') and in order.
The data is birthdays, so the order should be just based off of Month and Day and shouldn't care about the year.
For example, the data here is
Feb 20, Jul 16, Nov 18, Jan 1, Aug 27
current day= July 28
I want the output to look like
Aug 27, Nov 18, Jan 1, Feb 20, Jul 16
I've looked through many examples and documentations and tried some methods
SELECT * FROM table WHERE birthdays>date('now')
*birthdays are the column where dates are stored*
This gives all the data after ('now') as an output, but it orders it by year as well. Hence, the output will be none since none of the years are greater than current year. I wanna take the year out of the equation and just order it by Month and Day.
How can I do it?
You don't need a WHERE clause because all rows of the table should be returned.
What you want is a proper ORDER BY clause:
SELECT *
FROM tablename
ORDER BY strftime('%m-%d', birthdays) > strftime('%m-%d', 'now') DESC,
strftime('%m-%d', birthdays);
See the demo.
According to the sample data and the expected output you posted, you want to find all birthdays that will occur this year after the date of today. You may use the strftime function to extract month and day as the following:
Select user_id, DOB
From your_table
Where strftime('%m-%d',DOB) > strftime('%m-%d',date())
Order By strftime('%m-%d',DOB)
See a demo from db-fiddle.
you can select the dates, by taking he day of birth, adding the current year ( or the next one if it is smalerer tan the current date)and then selecting the dates that are bigger than the current date limiting 10
SELECT user_id, DOB
FROM your_table
ORDER BY
CASE WHEN date(strftime('%Y', date('now')) || strftime('-%m-%d', DOB)) > DATE() then date(strftime('%Y', date('now')) || strftime('-%m-%d', DOB))
ELSE date(strftime('%Y', date('now','+1 years')) || strftime('-%m-%d', DOB)) END
LIMIT 10;
user_id DOB
5 1996-08-27
10 1996-08-27
15 1996-09-27
13 2008-10-18
3 2008-11-18
8 2008-11-18
4 1998-01-02
9 1998-01-02
14 1998-01-02
1 2003-01-31
db<>fiddle here

Last year occurrence of date and month

I am looking to find a way to find the last time the day and year combination has happened.
I have a long list of dates and I want to find out what year the date and month has last occurred.
ie. 01/01 has happened in 2018 so I want 2018 as the output.
31/12 has not happened in 2018 yet, the last time is happened was in 2017, so I want 2017 as the output.
Table 1
(01/01/2015),
(01/01/2016),
(31/12/2015),
(25/07/2004)
Return table 2
(01/01/2015, 01/01/2018),
(01/01/2016, 01/01/2018),
(31/12/2015, 31/12/2017),
(25/07/2004, 25/07/2017)
OR even just return
(01/01/2015, 2018),
(01/01/2016, 2018),
(31/12/2015, 2017),
(25/07/2004, 2017)
Is this what you want?
select t2.*,
(case when month(col) < month(current_date) or
(month(col) < month(current_date) and day(col) <= day(current_date))
then year(current_date)
else 1 + year(current_date)
end)
from table2 t2;
This is using a reasonable set of date/time functions. These can vary by database.
To filter the month and year of a given date to the current date you can use:
SELECT *
FROM YourTable
WHERE month(date) = month(get_some_date()) and year(date) = year(get_somedate())
Here you can replace get_some_date to your function logic.

How to get how many days passed since start of this year?

I have a query which uses needs to know how many days passed since 1st of January in the current year.
Which means that if the query runs for example in:
2nd Jan 2017 than it should return 2 (as 2 days passed since 1st Jan
2017).
10th Feb 2016 than it should return 41 (as 41 days passed since 1st
Jan 2016).
basically it needs to take Current Year from Curent Date and count the days since 1/1/(Year).
i have the current year with: SELECT EXTRACT(year FROM CURRENT_DATE);
I created the 1st of Jan with:
select (SELECT EXTRACT(year FROM CURRENT_DATE)::text || '-01-01')::date
How do I get the difference from this date to Current_Date?
Basically this question can be Given two dates, how many days between them?
Something like age(timestamp '2016-01-01', timestamp '2016-06-15') isn't good because I need the result only in days. while age gives in years,months and days.
An easier approach may be to extract the day of year ("doy") field from the date:
db=> SELECT EXTRACT(DOY FROM CURRENT_DATE);
date_part
-----------
41
And if you need it as a number, you could just cast it:
db=> SELECT EXTRACT(DOY FROM CURRENT_DATE)::int;
date_part
-----------
41
Note: The result 41 was produced by running the query today, February 9th.
Given two dates, how many days between them
Just subtract one from the other.
In your case you could just round the current_date to the start of the year and subtract that from the current date:
select current_date - date_trunc('year', current_date)::date
The ::date cast is necessary to get the result as an integer, otherwise the result will be an interval.
Another solution is to use DATEDIFF
SELECT DATE_PART('day', now()::timestamp - '2016-01-01 00:00:00'::timestamp);

SQL that list all birthdays within the next and previous 14 days

I have a MySQL member table, with a DOB field which stores all members' dates of birth in DATE format (Notice: it has the "Year" part)
I'm trying to find the correct SQL to:
List all birthdays within the next 14 days
and another query to:
List all birthdays within the previous 14 days
Directly comparing the current date by:
(DATEDIFF(DOB, now()) <= 14 and DATEDIFF(DOB, now()) >= 0)
will fetch nothing since the current year and the DOB year is different.
However, transforming the DOB to 'this year' won't work at all, because today could be Jan 1 and the candidate could have a DOB of Dec 31 (or vice versa)
It will be great if you can give a hand to help, many thanks! :)
#Eli had a good response, but hardcoding 351 makes it a little confusing and gets off by 1 during leap years.
This checks if birthday (dob) is within next 14 days. First check is if in same year. Second check is if its say Dec 27, you'll want to include Jan dates too.
With DAYOFYEAR( CONCAT(YEAR(NOW()),'-12-31') ), we are deciding whether to use 365 or 366 based on the current year (for leap year).
SELECT dob
FROM birthdays
WHERE DAYOFYEAR(dob) - DAYOFYEAR(NOW()) BETWEEN 0 AND 14
OR
DAYOFYEAR( CONCAT(YEAR(NOW()),'-12-31') ) - ( DAYOFYEAR(NOW()) - DAYOFYEAR(dob) ) BETWEEN 0 AND 14
Here's the simplest code to get the upcoming birthdays for the next x days and previous x days
this query is also not affected by leap-years
SELECT name, date_of_birty
FROM users
WHERE DATE(CONCAT(YEAR(CURDATE()), RIGHT(date_of_birty, 6)))
BETWEEN
DATE_SUB(CURDATE(), INTERVAL 14 DAY)
AND
DATE_ADD(CURDATE(), INTERVAL 14 DAY)
My first thought was it would be easy to just to use DAYOFYEAR and take the difference, but that actually gets kinda trick near the start/end of a yeay. However:
WHERE
DAYOFYEAR(NOW()) - DAYOFYEAR(dob) BETWEEN 0 AND 14
OR DAYOFYEAR(dob) - DAYOFYEAR(NOW()) > 351
Should work, depending on how much you care about leap years. A "better" answer would probably be to extract the DAY() and MONTH() from the dob and use MAKEDATE() to build a date in the current (or potential past/following) year and compare to that.
Easy,
We can obtain the nearer birthday (ie the birthday of this year) by this code:
dateadd(year,datediff(year,dob,getdate()),DOB)
use this in your compares ! it will work.
There are a number of options, I would first try to transform by number of years between current year and row's year (i.e. Add their age).
Another option is day number within the year (but then you have still to worry about the rollover arithmetic or modulo).
This is my query for the 30 days before check:
select id from users where
((TO_DAYS(concat(DATE_FORMAT(NOW(),'%Y'), '-', DATE_FORMAT(date_of_birth, '%m-%d')))-TO_DAYS(NOW()))>=-30
AND (TO_DAYS(concat(DATE_FORMAT(NOW(),'%Y'), '-', DATE_FORMAT(date_of_birth, '%m-%d')))-TO_DAYS(NOW()))<=0)
OR (TO_DAYS(concat(DATE_FORMAT(NOW(),'%Y'), '-', DATE_FORMAT(date_of_birth, '%m-%d')))-TO_DAYS(NOW()))>=(365-31)
and 30 days after:
select id from users where
((TO_DAYS(NOW())-TO_DAYS(concat(DATE_FORMAT(NOW(),'%Y'), '-', DATE_FORMAT(date_of_birth, '%m-%d'))))>=-31
AND (TO_DAYS(NOW())-TO_DAYS(concat(DATE_FORMAT(NOW(),'%Y'), '-', DATE_FORMAT(date_of_birth, '%m-%d'))))<=0)
OR (TO_DAYS(NOW())-TO_DAYS(concat(DATE_FORMAT(NOW(),'%Y'), '-', DATE_FORMAT(date_of_birth, '%m-%d'))))>=(365-30)
My solution is as follow:
select cm.id from users cm where
date(concat(
year(curdate()) - (year(subdate(curdate(), 14)) < year(curdate())
and month(curdate()) < month(cm.birthday)) + (year(adddate(curdate(), 14)) > year(curdate())
and month(curdate()) > month(cm.birthday)), date_format(cm.birthday, '-%m-%d'))) between subdate(curdate(), 14)
and adddate(curdate(), 14);
It looks like it works fine when the period captures the current and next year or the current and previous year