Selecting 2 date sets from the same dataset? - sql

Say I have a query that selects all the sales from the past 90 days. I want to be able to isolate certain rows on a case/when basis, and can't quite figure out how to do this. The case statement is depending on dates, so: If the date falls between 3/1 and 5/31, then I want to select the sales from any month ends (3/31, 4/30, 5/31 and TODAY) otherwise, if the date is not between 3/1 and 5/31, then I just want to select the past 3 month-ends.
What I tried so far is inserting a Case/When statement in the WHERE clause, but that doesn't seem kosher. Is there another way to go about this?
For reference, the #monthends table contains the following single column:
monthends
2019-03-31
2019-02-28
2019-01-31
and the #insideRule table contains similarly:
insiderRule
2019-03-31
2019-04-22
The query:
SELECT *
FROM mytable
WHERE asofdate IN
CASE WHEN asofdate BETWEEN '3-1-2019' AND '5-31-2019' THEN
(SELECT * FROM #insideRule)
ELSE
(SELECT * FROM #monthends)
END
When I execute the above, I get syntax errors around "IN"

You want exists not case expression :
IF EXISTS (SELECT 1 FROM mytable WHERE aasofdate BETWEEN '2019-03-01' AND '2019-05-31')
SELECT *
FROM #insideRule
ELSE
SELECT *
FROM #monthends

I am thinking you want something like this:
SELECT ir.*
FROM #insideRule ir
WHERE getdate() >= '2019-03-01' AND
getdate() < '2019-06-01'
UNION ALL
SELECT me.*
FROM #monthends me
WHERE getdate() < '2019-03-01' OR
getdate() >= '2019-06-01';
This assumes that the two tables have the same columns in the same order with compatible types.

Related

Select list of dates from given range in Firebird

I'm designing a report that returns PurchaseOrder due in future week.
Query that I've added below returns PurchaseOrder due for a particular Commodity, AmountDue and its DeliveryDate.
Obviously it only returns PO_Dates that are in the table. What I want is to also include dates where no PO is expected, i.e. null for those cell.
To me one possibility is to LEFT JOIN the dataset with set of dates of future week on Date column, that will eventually make the result null where no Purchase Order is expected.
In Firebird I don't know how to select list of week long dates and then use it in the join.
SELECT
PURCHASE_ORDER_DET.COMMODITYID AS COM_ID,
PURCHASE_ORDER_DET.DELIVERYDATE + CAST ('29.12.1899' AS DATE) as DLV_DATE,
SUM(PURCHASE_ORDER_DET.REQQUANTITY) as DLV_DUE
FROM
PURCHASE_ORDER_DET
LEFT JOIN PURCHASE_ORDER_HDR on PURCHASE_ORDER_HDR.POH_ID =
PURCHASE_ORDER_DET.POH_ID
WHERE
PURCHASE_ORDER_DET.COMMODITYID = 1
AND PURCHASE_ORDER_HDR.STATUS in (0,1,2)
AND PURCHASE_ORDER_DET.DELIVERYDATE + CAST ('30.12.1899' AS TIMESTAMP) >= '3.01.2019'
AND PURCHASE_ORDER_DET.DELIVERYDATE + CAST ('30.12.1899' AS TIMESTAMP) <= '9.01.2019'
AND PURCHASE_ORDER_DET.DELETED is NULL
Group by
PURCHASE_ORDER_DET.COMMODITYID,
PURCHASE_ORDER_DET.DELIVERYDATE
DataSet
COM_ID DLV_DATE DLV_DUE
1 3.01.2019 50.000000
1 5.01.2019 10.000000
Expected
COM_ID DLV_DATE DLV_DUE
1 3.01.2019 50.000000
1 4.01.2019 null
1 5.01.2019 10.000000
1 6.01.2019 null
1 7.01.2019 null
1 8.01.2019 null
1 9.01.2019 null
Ignoring your odd use of datatypes*, there are several possible solutions:
Use a 'calendar' table that contains dates, and right join to that table (or left join from that table). The downside of course is having to populate this table (but that is a one-off cost).
Use a selectable stored procedure to generate a date range and join on that.
Generate the range in a recursive common table expression in the query itself
Option 1 is pretty self-explanatory.
Option 2 would look something like:
CREATE OR ALTER PROCEDURE date_range(startdate date, enddate date)
RETURNS (dateval date)
AS
BEGIN
dateval = startdate;
while (dateval <= enddate) do
BEGIN
suspend;
dateval = dateval + 1;
END
END
And then use this in your query like:
select date_range.dateval, ...
from date_range(date'2019-01-03', date'2019-01-09') -- use date_range(?, ?) for parameters
left join ...
on date_range.dateval = ...
Option 3 would look something like:
WITH RECURSIVE date_range AS (
SELECT date'2019-01-03' dateval -- start date, use cast(? as date) if you need a parameter
FROM rdb$database
UNION ALL
SELECT dateval + 1
FROM date_range
WHERE dateval < date'2019-01-09' -- end date use ? if you need a parameter
)
SELECT *
FROM date_range
LEFT JOIN ...
ON date_range.dateval = ...
Recursive common table expressions have a maximum recursion depth of 1024, which means that it isn't suitable if you need a span wider than 1024 days.
*: I'd suggest that you start using DATE instead of what looks like the number of days since 30-12-1899. That avoids having to do awkward calculations like you do now. If you do need those number of days, then you can for example use datediff(DAY FROM date'1899-12-30' TO somedatevalue) or somedatevalue - date'1899-12-30' to convert from date to that numeric value.

How can I pivot my T-SQL query output to get this specific table?

I am running a T-SQL query on SQL Server 2014. The query and its output are given below:
Use MyDatabase
SELECT
ID,
ArrivalMonth,
DateOfBirth
FROM [View1]
WHERE [ArrivalMonth] between '2017-01-01' and '2018-05-01'
The output of the above query looks like this (extract):
ID ArrivalMonth DateOfBirth
101 2017-01-01 1974-05-30
105 2017-05-01 1967-03-05
125 2017-05-01 NULL
... ... ...
I need a T-SQL query to give me the following output (based on the output above):
ArrivalMonth Number_Of_Bookings Number_Of_DOB_Captured
2017-01-01 130 110
2017-02-01 90 85
... ... ...
2018-05-01 115 70
The first column is the ArrivalMonth. Number_Of_Bookings is the count of number of records from the above query. Number_Of_DOB_Captured is the count of DateOfBirth which is NOT NULL.
I think may be the Pivot query might be the solution but I am confused as to how to execute it in this scenario.
You may left join a calendar table containing all the months to your current table, and then aggregate:
WITH months AS (
SELECT '2017-01-01' AS month UNION ALL
SELECT '2017-02-01' UNION ALL
...
SELECT '2017-12-01'
)
SELECT
m.month,
COUNT(*) AS Number_Of_Bookings,
COUNT(v.DateOfBirth) AS Number_Of_DOB_Captured
FROM months m
LEFT JOIN [View1] v
ON m.month = v.ArrivalMonth
WHERE
v.ArrivalMonth BETWEEN '2017-01-01' AND '2018-05-01'
GROUP BY
m.month;
The calendar table may be necessary here if it could be possible that, for some reason, a given arrival month have no data associated with it in your view. If you are certain that the view would always contain data for every month, then you may aggregate directly on your table without joining.
you can use count(Number_Of_Bookings), count(DateOfBirth) and group by ArrivalMonth
So you count the number of non null values for each different ArrivalMonth.
the query :
Select ArrivalMonth
, count(Number_Of_Bookings)
, count(DateOfBirth)
FROM [View1]
WHERE [ArrivalMonth] between '2017-01-01' and '2018-05-01'
group by ArrivalMonth

Select Data using DateRange in SQL

I am querying in my Table for Distinct Product Details. Now I have to filter Data based upon Date Column present in Table. The column having Dates have Data Type Varchar2. But I am not getting any result whereas Data is present in that Daterange. WEEK_DATE is my Date Column.
select distinct PRODUCT
from Table1
where WEEK_DATE between '12/31/2012' and '06/19/2017'
Some Sample Dates
2014-03-31
2014-09-01
2014-12-15
2014-12-22
You can try the following query :
select distinct PRODUCT from Table1
where cast(WEEK_DATE as date) between '12/31/2012' and '06/19/2017'
I would start by switching to standard date formats:
select distinct product
from Table1
where week_date >= '2012-12-31' and
week_date < '2017-06-20';
This will probably fix your problem. You query would return no rows if the comparisons were made as strings rather than dates.

Table not modified since date

I have a table with following fields.
Part ID, Quantity, Last modified date
I am using the following simple query to retrieve data, how can I add more criteria to show that quantity in hand 0 and part did not modified since 01/01/2015 till now.
SELECT *
FROM table001
where quantity_on_hand=0
Just add another filter in your WHERE clause like below:
SELECT *
FROM table001
WHERE quantity_on_hand=0
AND ModificationDate <= '20150101' -- Date format = 'YYYYMMDD'
I think this will do what you're looking for
SELECT * FROM table001 WHERE quantity_on_hand = 0 AND last_modified_date <= '2015-01-01'
If you are looking to combine multiple conditions you could use AND,OR operators after WHERE clause.
Here I think you might be looking for:
SELECT
*
FROM
table001
WHERE
qunatity_on_hand=0 AND last_modified_date <= '2015-01-01';

Efficient way to query separate days of data?

I want to query statistics using SQL from 3 different days (in a row). The display would be something like:
15 users created today, 10 yesterday, 12 two days ago
The SQL would be something like (for today):
SELECT Count(*) FROM Users WHERE created_date >= '2012-05-11'
And then I would do 2 more queries for yesterday and the day before.
So in total I'm doing 3 queries against the entire database. The format for created_date is 2012-05-11 05:24:11 (date & time).
Is there a more efficient SQL way to do this, say in one query?
For specifics, I'm using PHP and SQLite (so the PDO extension).
The result should be 3 different numbers (one for each day).
Any chance someone could show some performance numbers in comparison?
You can use GROUP BY:
SELECT Count(*), created_date FROM Users GROUP BY created_date
That will give you a list of dates with the number of records found on that date. You can add criteria for created_date using a normal WHERE clause.
Edit: based on your edit:
SELECT Count(*), created_date FROM Users WHERE created_date>='2012-05-09' GROUP BY date(created_date)
The best solution is to use GROUP BY DAY(created_date). Here is your query:
SELECT DATE(created_date), count(*)
FROM users
WHERE created_date > CURRENT_DATE - INTERVAL 3 DAY
GROUP BY DAY(created_date)
This would work I believe though I have no way to test it:
SELECT
(SELECT Count(*) FROM Users WHERE created_date >= '2012-05-11') as today,
(SELECT Count(*) FROM Users WHERE created_date >= '2012-05-10') as yesterday,
(SELECT Count(*) FROM Users WHERE created_date >= '2012-05-11') as day_before
;
Use GROUP BY like jeroen suggested, but if you're planning for other periods you can also set ranges like this:
SELECT SUM(IF(created_date BETWEEN '2012-05-01' AND NOW(), 1, 0)) AS `this_month`,
SUM(IF(created_date = '2012-05-09', 1, 0)) AS `2_days_ago`
FROM ...
As noted below, SQLite doesn't have IF function but there is CASE instead. So this way it should work:
SELECT SUM(CASE WHEN created_date BETWEEN '2012-05-01' AND NOW() THEN 1 ELSE 0 END) AS `this_month`,
SUM(CASE created_date WHEN '2012-05-09' THEN 1 ELSE 0 END) AS `2_days_ago`
FROM ...