SQL to Count status text and change some of those status texts (and their count) based on a date - sql

Spent a while looking for this but did not quite find a solution. The problem: get counts for the status value in one column. Pretty simple so far, how ever I also want to change/ignore some of them when the DueDate (and it can be null) is past due based on current date and NOT complete. Also include empty strings for the status. Example Data (forgive the date show as text...assume it is a SQL date type - just wanted to make this more readable):
Current Date Apr : April 4, 2016
Data In DB Table DispatchStatus
Status Due Date
=======================================
Complete Mar 1, 2015 <-- would not be Late since Complete
Complete null
Complete July 12, 2016
Complete July 16, 2016
Started Mar 3, 2017
Started null
Started Feb 9, 2015 <-- Late
OnDevice June, 2016
OnDevice Dec 3, 2015 <-- Late
Dispatched Nov 16, 2015 <-- Late
Dispatched null
Dispatched Nov 20, 2016
Nov 15, 2017
null
Jan 15, 2016 <-- Late
The query should return:
Status Count
=========================
Complete 4
Started 2
OnDevice 1
2
Dispatched 2
Late 4
Thanks!

Use a CASE expression together with COUNT:
DECLARE #currentDate DATE = '20160404'
SELECT
Status =
CASE
WHEN DueDate <= #currentDate AND ISNULL(Status, '') <> 'Complete' THEN 'Late'
ELSE Status
END,
COUNT(*)
FROM DispatchStatus
GROUP BY
CASE
WHEN DueDate <= #currentDate AND ISNULL(Status, '') <> 'Complete' THEN 'Late'
ELSE Status
END
I think your sample current date should be Apr 4, 2016 based on your sample output.

Related

SQL generate a table, count or function for graph

Firstly I have no code to show (but for good reason). I need a pointer or direction before I try again as I have failed a few times already trying to create a recursive function and so on. Kind of given up and thought I would ask you experts as I am lost and stressed.
My Scenario is this.
Im creating a graph in PHP using Json and thats all fine. However the data I need is my issue.
I have records that have a start and an end date.
Example
ID 14
Start_Date 03/08/2021
End_Date 07/08/2021
Running a stored procedure to grab a records and count between 1 Aug to 10 Aug would display the above as a single record.
Im trying to create a line chart that would have 1 Aug to 2 Aug null then 3 Aug through 7 Aug displaying 1 and finally 8 to 10 Aug null.
1 Aug 2021 0
2 Aug 2021 0
3 Aug 2021 1
4 Aug 2021 1
5 Aug 2021 1
6 Aug 2021 1
7 Aug 2021 1
8 Aug 2021 0
9 Aug 2021 0
10 Aug 2021 0
Is this possible as I have nearly given up.
The nearest I came was using a loop to create a temporary table and inserting records was NOT pretty and certain was embarrassing. If I recreated and posted it here I would die of shame for sure.
So if anyone can point me in the right direction, offer a suggestion or anything like this would be very much appreciated.
Thank you for reading.
You need to start with a list of dates. There are many ways to generate such a list -- perhaps you have an existing table, or your database supports a function. SQL (in general) supports recursive CTEs, which is an alternative method.
Once you have the dates, you can use left join and group by to get the counts you want. Here is an example using MySQL syntax:
with recursive dates as (
select date('2021-08-01') as dte
union all
select dte + interval 1 day
from dates
where dte < '2021-08-10'
)
select d.dte, count(t.id)
from dates d left join
t
on d.dte between t.start_date and t.end_date
group by d.dte;
Here is a db<>fiddle.

Combine Metric and Dimension In Formula (Datastudio)

I am trying to figure out how to create a column where...
clicks * (CASE
When Date <= "Jan 1, 2020" then 5
when Date >= "Jan 2, 2020" then 10
end)
But the error I am getting is this:
Sorry, calculated fields can't mix metrics (aggregated values) and
dimensions (non-aggregated values). Please check the aggregation types
of the fields used in this formula
Date is the dimension and clicks are the metric.
What the result should look like:
DATE ----- CLICKS --------- RESULT
Jan 1, 2020 100 500
Jan 1, 2020 40 200
Jan 1, 2020 10 50
Jan 2, 2020 30 300
Jan 1, 2020 90 900
Is there a way to change Date into a Metric, or is there another way to approach this problem?
I think the way the case statement is written is fine. You may need to check the data type for the Date dimension, and it should be in the "Date" format so that data studio knows to treat that data as a Date and then write the date in the case statement in the same format as well.
If you wrap the CASE inside a MAX you'll get back a number and you'll avoid the error.
clicks * MAX(
(CASE
WHEN Date <= "Jan 1, 2020" then 5
WHEN Date >= "Jan 2, 2020" then 10
END))
clicks * MAX(
(CASE
WHEN Date <= "Aug 15, 2021" then 2
WHEN Date > "Aug 15, 2021" then 4
END))
This worked!

SQL to separate YYYY MM to fiscal year

I have a column which states month and year YYYY MM. I've separated those into two columns (Year and Month). The problem is, the year is the calendar year whereas ideally I need the fiscal year I use (Apr 01 to Mar 31 - This will never change).
Other solutions I've seen are based on date format, whereas my original column is string.
I need a statement that returns the fiscal year for my new year column instead of the calendar year.
My current statement is:
Select Month,
parsename(replace(Month,' ','.'),1) as MonthM,
parsename(replace(Month,' ','.'),2) as Year
FROM TblTrade
Which works to separate the columns.
So expected results would be for example:
Feb 15 becomes Feb and 2015.
Apr 15 becomes Apr and 2016.
Please advise.
Sql server:
declare #date datetime = getdate();
select
(YEAR(DATEADD(Month,-((DATEPART(Month,#date)+5) %12),#date))) AS Financial_Year
Assuming April is month 1
Try this
select case
when to_char(to_date(column_name,'yyyy mm'),'mm') between 01 and 03
then to_char(trunc(to_date(column_name,'yyyy mm')),'yyyy')-1
else to_number(to_char(trunc(to_date(column_name,'yyyy mm')),'yyyy')) end
fiscal_year
from table_name
I'm using oracle db
This will work when column is string and has valid data i.e date in format like yyyy mm
Since you've read those other articles (you should really mention what research you've done in your question) and you're still having problems, I've had a play for you.
If I understand correctly, you have a varchar with YYYY MM eg
2015 01
2015 02
2015 03
2015 04
etc And you want
Jan 2014
Feb 2014
Mar 2014
Apr 2015
Here goes...
Setup some test data
DROP TABLE IF EXISTS #Test;
WITH Dates AS (
SELECT CAST(GETDATE() AS DATE) AS Date
UNION ALL
SELECT DATEADD(MONTH, -1, Date) FROM Dates
WHERE Date > '20140101'
)
SELECT DISTINCT
CONVERT(VARCHAR(4), YEAR(Date)) + ' ' +RIGHT(CONVERT(VARCHAR(6), Date, 112), 2) YearMonth
INTO #Test
FROM Dates
OPTION (MAXRECURSION 0);
SELECT * FROM #Test
YearMonth
---------
2013 12
2014 01
2014 02
2014 03
2014 04
2014 05
etc
Find Fiscal Year
SELECT
LEFT(YEARMONTH, 4) Year
,RIGHT(YEARMONTH, 2) Month
,LEFT(DATENAME(MONTH , DateAdd( month , CONVERT(INT,RIGHT(YEARMONTH, 2)) , -1 )), 3) MonthName
,IIF(CONVERT(INT, RIGHT(YEARMONTH, 2)) >= 4, CONVERT(INT,LEFT(YEARMONTH, 4)), CONVERT(INT,LEFT(YEARMONTH, 4)-1 )) FiscalYear
FROM #TEST
Year Month MonthName FiscalYear
---- ----- --------- -----------
2013 12 Dec 2013
2014 01 Jan 2013
2014 02 Feb 2013
2014 03 Mar 2013
2014 04 Apr 2014
2014 05 May 2014
2014 06 Jun 2014
etc
You could put the year/month parsing in a sub query just to make the code cleaner and some of the nasty formatting could be replaced with FORMAT since you're on 2012.
Hope this is what you're after and helps.
Since you included the Tableau tag, I'll describe the Tableau approach -- which is a little different than the other answers since you tend to specify what you want to Tableau, and let its driver generate the necessary SQL for your database.
First, it will work best if you have a single field that has datatype DATE instead of separate fields for month and year.
You can then roll up dates to the nearest year, month, day etc (actually truncating to the beginning of the period) or extract specific parts of dates year, month, day etc as needed for grouping/display.
The added benefit of working with a true DATE datatype is that you can tell Tableau the beginning of your fiscal year for each data source, and it will sort dates appropriately. Just right click on a data source and set the date properties. You can also set the start of the week and the date format.

Code erases previous month record while updating current month record

Using 11gR2, windows 7 client machine. I need to update the table missing_volume (below), where I need to calculate the estimated_missing column. The calculation of estimated_missing column for current month needs previous month numbers (as commented inside the code below). I want the output like the first table. Notice the records start from January, hence estimated_missing for January can't be calculated, but for the the rest of the months it can be done by simply changing 'yr' and 'mnth' (commented inside the code towards the end).
yr mnth location volume actual_missing expected_missing estimated_missing
2013 January loc1 48037 24 57
2013 February loc1 47960 3660 53 24
2013 March loc1 55007 78 57 28
2013 April loc1 54345 72 58 77
The code:
UPDATE missing_volume g
SET
g.estimated_missing =
(
SELECT
CASE
-- If the previous month actual_missing number is less than 2 times than that of the previous month expected_missing number,
-- then for current month:
-- estimated_missing = previous_month(actual_missing/volume)*current_month(volume)
--
-- Else, for current month:
-- estimated_missing = previous_month(estimated_missing/volume)*current_month(volume)
WHEN (NVL(p.actual_missing, 0)/NVL(p.expected_missing, 1)) <= 2.0
THEN ROUND(NVL(p.actual_missing, 0)*( NVL(g.volume, 0)/NVL(p.volume, 1) ), 0)
ELSE
ROUND(NVL(p.estimated_missing, 0)*( NVL(g.volume, 0)/NVL(p.volume, 1) ), 0)
END
FROM
(
SELECT
yr,
mnth,
loc_cd,
volume,
actual_missing,
expected_missing,
estimated_missing
FROM missing_volume
WHERE yr = TRIM(TO_CHAR(ADD_MONTHS(SYSDATE, -2), 'YYYY')) -- For February it is -4, for March it is -3, for April it is -2
AND UPPER(mnth) = UPPER(TRIM(TO_CHAR(ADD_MONTHS(SYSDATE, -2), 'Month'))) -- For February it is -4, for March it is -3, for April it is -2
)p
WHERE g.yr = TRIM(TO_CHAR(ADD_MONTHS(SYSDATE, -1), 'YYYY')) -- For February it is -3, for March it is -2, for April it is -1
AND UPPER(g.mnth) = UPPER(TRIM(TO_CHAR(ADD_MONTHS(SYSDATE, -1), 'Month'))) -- For February it is -3, for March it is -2, for April it is -1
AND g.volume IS NOT NULL
AND TRIM(p.location) = TRIM(g.location)
);
The code does calculate correct number for 'estimated_missing' as I run the code for each month, but the problem is while updating the current month it also erases the record for previous month. E.g. as can be seen below, after I updated April the column only has the record for April, previous month record is gone, similarly updating March removed February, etc. I can't understand why it's happening!! Here is the output I get:
yr mnth location volume actual_missing expected_missing estimated_missing
2013 January loc1 48037 24 57
2013 February loc1 47960 3660 53
2013 March loc1 55007 78 57
2013 April loc1 54345 72 58 77
Will appreciate if any body can explain why it's happening (I mean where is the flaw in the code) and how to get the desired output (first table).
Thanks
You need to specify a where clause for your statement.
At the moment that WHERE clause is inside the braces. Your statement basically is:
UPDATE missing_volume g
SET
g.estimated_missing =
( [...]
); -- no where clause here
What you need is this:
UPDATE missing_volume g
SET
g.estimated_missing =
( [...]
)
WHERE g.yr = TRIM(TO_CHAR(ADD_MONTHS(SYSDATE, -1), 'YYYY'));
Haven't tested it myself but this should only update the row for the current month.

Multiple conditions within WHERE clause

This query is working fine:
SELECT * FROM tablename where Date >'20091109' and id='11';
But below this query does not return anything.
SELECT * FROM tablename
WHERE Date BETWEEN ('20091109' AND '20081010') AND id='11';
between ('20091109' and '20081010')
This is anything after 9th Nov 2008 and before 10th Oct 2008. Of course if show nothing.
Do you mean this which is 10 Oct 2008 to 8th Nov 2009 inclusive
Date >= '20081010' AND Date < '20091109'
or this which is 10 Oct 2008 to 9th Nov 2009 inclusive
Date >= '20081010' AND Date < '20091110'
Edit: Removed SQL Server references
SELECT * FROM
tablename
where Date between '20081010' and '20091109'
and id='11';