On SQL what would be the best way to do formulas? - sql

I have the following structure
Columns structure
The date columns contain hours as well. I need to calculate the time beetween start and date on the same case number.
Example if case 1 has 2 subcases I need to calculate from the start date of the first one until the end date of the second one and add that to another column as "actual fixed time".
The thing is there could be 2 or 10 or 20 or more subcases.
Any thoughts?

You will want to use DATEDIFF in conjunction with GROUP BY
SELECT DATEDIFF(hour, MIN(start_date), MAX(end_date)) as ActualFixedTime FROM YourTable GROUP BY Casenumber
This will give you your actual fixed time for each case. In order to display this result for each row of your subcases, you join on it when selected your table:
SELECT YourTable.*, FixedTimes.ActualFixedTime
FROM YourTable
INNER JOIN (SELECT Casenumber,
DATEDIFF(hour, MIN(start_date), MAX(end_date)) AS ActualFixedTime
FROM YourTable
GROUP BY Casenumber)
AS FixedTimes
ON YourTable.Casenumber = FixedTimes.Casenumber

Related

Finding id's available in previous weeks but not in current week

How to find if an id which was present in previous weeks but not available in current week on a rolling basis. For e.g
Week1 has id 1,2,3,4,5
Week2 has id 3,4,5,7,8
Week3 has id 1,3,5,10,11
So I found out that id 1 and 2 are missing in week 2 and id 2,4,7,8 are missing in week 3 from previous 2 weeks But how to do this on a rolling window for a large amount of data distributed over a period of 20+ years
Please find the sample dataset and expected output. I am expecting the output to be partitioned based on the week_end Date
Dataset
ID|WEEK_START|WEEK_END|APPEARING_DATE
7152|2015-12-27|2016-01-02|2015-12-27
8350|2015-12-27|2016-01-02|2015-12-27
7152|2015-12-27|2016-01-02|2015-12-29
4697|2015-12-27|2016-01-02|2015-12-30
7187|2015-12-27|2016-01-02|2015-01-01
8005|2015-12-27|2016-01-02|2015-12-27
8005|2015-12-27|2016-01-02|2015-12-29
6254|2016-01-03|2016-01-09|2016-01-03
7962|2016-01-03|2016-01-09|2016-01-04
3339|2016-01-03|2016-01-09|2016-01-06
7834|2016-01-03|2016-01-09|2016-01-03
7962|2016-01-03|2016-01-09|2016-01-05
7152|2016-01-03|2016-01-09|2016-01-07
8350|2016-01-03|2016-01-09|2016-01-09
2403|2016-01-10|2016-01-16|2016-01-10
0157|2016-01-10|2016-01-16|2016-01-11
2228|2016-01-10|2016-01-16|2016-01-14
4697|2016-01-10|2016-01-16|2016-01-14
Excepted Output
Partition1: WEEK_END=2016-01-02
ID|MAX(LAST_APPEARING_DATE)
7152|2015-12-29
8350|2015-12-27
4697|2015-12-30
7187|2015-01-01
8005|2015-12-29
Partition1: WEEK_END=2016-01-09
ID|MAX(LAST_APPEARING_DATE)
7152|2016-01-07
8350|2016-01-09
4697|2015-12-30
7187|2015-01-01
8005|2015-12-29
6254|2016-01-03
7962|2016-01-05
3339|2016-01-06
7834|2016-01-03
Partition3: WEEK_END=2016-01-10
ID|MAX(LAST_APPEARING_DATE)
7152|2016-01-07
8350|2016-01-09
4697|2016-01-14
7187|2015-01-01
8005|2015-12-29
6254|2016-01-03
7962|2016-01-05
3339|2016-01-06
7834|2016-01-03
2403|2016-01-10
0157|2016-01-11
2228|2016-01-14
Please use below query,
select ID, MAX(APPEARING_DATE) from table_name
group by ID, WEEK_END;
Or, including WEEK)END,
select ID, WEEK_END, MAX(APPEARING_DATE) from table_name
group by ID, WEEK_END;
You can use aggregation:
select t.*, max(week_end)
from t
group by id
having max(week_end) < '2016-01-02';
Adjust the date in the having clause for the week end that you want.
Actually, your question is a bit unclear. I'm not sure if a later week end would keep the row or not. If you want "as of" data, then include a where clause:
select t.id, max(week_end)
from t
where week_end < '2016-01-02'
group by id
having max(week_end) < '2016-01-02';
If you want this for a range of dates, then you can use a derived table:
select we.the_week_end, t.id, max(week_end)
from (select '2016-01-02' as the_week_end union all
select '2016-01-09' as the_week_end
) we cross join
t
where t.week_end < we.the_week_end
group by id, we.the_week_end
having max(t.week_end) < we.the_week_end;

FirebirdSQL Unique & Max (or MaxValue)

I want to run a report from sales of any customer that has ordered in the last two years.
I can run a report of all invoices dated within two years then remove duplicates in excel, but I would rather do it directly within (Firebird) SQL
I can use a WHERE date < 1 Jan 2015 (2 years or thereabours), but how do I get it to only show the customer once? I thought if I used MAX(Date) therefore showing the most recent date in that two year period. Where am I going wrong? I believe I need to use a UNIQUE() function like UNIQUE(ORDERCUSTOMER) within the SELECT clause.
SELECT
FINANCIALSALESINVOICES.TRANSACTIONDATE,
FINANCIALSALESINVOICES.INVOICECUSTOMER,
FINANCIALSALESINVOICES.ORDERCUSTOMER,
FINANCIALSALESINVOICES.INVOICENUMBER,
FINANCIALSALESINVOICES.SOURCENUMBER,
MAX(FINANCIALSALESINVOICES.TRANSACTIONDATE)
FROM FINANCIALSALESINVOICES
WHERE (FINANCIALSALESINVOICES.TRANSACTIONDATE>={d '2015-01-01'})
ORDER BY FINANCIALSALESINVOICES.INVOICECUSTOMER, FINANCIALSALESINVOICES.TRANSACTIONDATE
I did having it showing the max date for each instance of invoice in the past two years, but now can't fine that file or replicate it.
One approach is to use a subquery in the WHERE clause which checks for the most recent invoice:
SELECT
t.TRANSACTIONDATE,
t.INVOICECUSTOMER,
t.ORDERCUSTOMER,
t.INVOICENUMBER,
t.SOURCENUMBER
FROM FINANCIALSALESINVOICES t
WHERE t.TRANSACTIONDATE >= date '2015-01-01' AND
t.TRANSACTIONDATE = (SELECT MAX(f.TRANSACTIONDATE)
FROM FINANCIALSALESINVOICES f
WHERE t.ORDERCUSTOMER = f.ORDERCUSTOMER AND
f.TRANSACTIONDATE >= date '2015-01-01')
ORDER BY t.INVOICECUSTOMER,
t.TRANSACTIONDATE
With Firebird 3 you can use row_number() to assign a unique value to each row within a group (partition), that value can then be filtered on:
select
a.TRANSACTIONDATE,
a.INVOICECUSTOMER,
a.ORDERCUSTOMER,
a.INVOICENUMBER,
a.SOURCENUMBER
from (
select
TRANSACTIONDATE,
INVOICECUSTOMER,
ORDERCUSTOMER,
INVOICENUMBER,
SOURCENUMBER,
row_number() over (partition by INVOICECUSTOMER, order by TRANSACTIONDATE desc) as rownr
from FINANCIALSALESINVOICES
where TRANSACTIONDATE >= date '2015-01-01'
) a
where a.rownr = 1
order by a.INVOICECUSTOMER, a.TRANSACTIONDATE
See also Window (Analytical) Functions in the Firebird 3 release notes.

Calculating business days in Teradata

I need help in business days calculation.
I've two tables
1) One table ACTUAL_TABLE containing order date and contact date with timestamp datatypes.
2) The second table BUSINESS_DATES has each of the calendar dates listed and has a flag to indicate weekend days.
using these two tables, I need to ensure business days and not calendar days (which is the current logic) is calculated between these two fields.
My thought process was to first get a range of dates by comparing ORDER_DATE with TABLE_DATE field and then do a similar comparison of CONTACT_DATE to TABLE_DATE field. This would get me a range from the BUSINESS_DATES table which I can then use to calculate count of days, sum(Holiday_WKND_Flag) fields making the result look like:
Order# | Count(*) As DAYS | SUM(WEEKEND DATES)
100 | 25 | 8
However this only works when I use a specific order number and cant' bring all order numbers in a sub query.
My Query:
SELECT SUM(Holiday_WKND_Flag), COUNT(*) FROM
(
SELECT
* FROM
BUSINESS_DATES
WHERE BUSINESS.Business BETWEEN (SELECT ORDER_DATE FROM ACTUAL_TABLE
WHERE ORDER# = '100'
)
AND
(SELECT CONTACT_DATE FROM ACTUAL_TABLE
WHERE ORDER# = '100'
)
TEMP
Uploading the table structure for your reference.
SELECT ORDER#, SUM(Holiday_WKND_Flag), COUNT(*)
FROM business_dates bd
INNER JOIN actual_table at ON bd.table_date BETWEEN at.order_date AND at.contact_date
GROUP BY ORDER#
Instead of joining on a BETWEEN (which always results in a bad Product Join) followed by a COUNT you better assign a bussines day number to each date (in best case this is calculated only once and added as a column to your calendar table). Then it's two Equi-Joins and no aggregation needed:
WITH cte AS
(
SELECT
Cast(table_date AS DATE) AS table_date,
-- assign a consecutive number to each busines day, i.e. not increased during weekends, etc.
Sum(CASE WHEN Holiday_WKND_Flag = 1 THEN 0 ELSE 1 end)
Over (ORDER BY table_date
ROWS Unbounded Preceding) AS business_day_nbr
FROM business_dates
)
SELECT ORDER#,
Cast(t.contact_date AS DATE) - Cast(t.order_date AS DATE) AS #_of_days
b2.business_day_nbr - b1.business_day_nbr AS #_of_business_days
FROM actual_table AS t
JOIN cte AS b1
ON Cast(t.order_date AS DATE) = b1.table_date
JOIN cte AS b2
ON Cast(t.contact_date AS DATE) = b2.table_date
Btw, why are table_date and order_date timestamp instead of a date?
Porting from Oracle?
You can use this query. Hope it helps
select order#,
order_date,
contact_date,
(select count(1)
from business_dates_table
where table_date between a.order_date and a.contact_date
and holiday_wknd_flag = 0
) business_days
from actual_table a

Query to Find First and Last Day of Current Month excluding sunday in sql

i need to get the data of monthly report which should exclude Sunday in all the weeks...where am not able to exclude Sunday. Kindly check the below query:
select Name,Actual_Hours,round((Actual_Hours/((DATEDIFF(DAY,'12-01-2016' ,'12-14-2016')+1)*8.5))*100,0)as percentage
from (select Name , SUM(actual_Hours) Actual_Hours
from ( select Name,ACTUAL_HOURS=sum(DATEPART(hh,[Time_Taken_in_Min]))+sum(DATEPART(MINUTE,[Time_Taken_in_Min])) /60
FROM [HR_Admin].[dbo].[Mst_Daily_Report_Pds] where date between '12-01-2016' and '12-14-2016' and (DATENAME(weekday,Date)) not in ('sunday')
GROUP BY Name )o group by Name )a order by Actual_Hours desc
Don't know exactly what error you are getting but I don't see the issue in SQL, but I believe it's because of the use of DATEDIFF in the select query instead of using the actual days.
Check the below query which uses the actual days (already removed Sunday in Where clause) and using less subquery. You can further improve this by taking only the time factor in the innermost query and then rounding off after summing all the hours.
Another suggestion is to avoid using the column name as reserved word like Date as it could create confusion, or using the column in Square brackets like [Date].
select Name
, sum(Actual_Hours) as Actual_Hours
, round((sum(Actual_Hours)/COUNT([Day])*8.5)*100,0) as percentage
from (select Name
, DATEPART(DAYOFYEAR,[Date]) as [Day]
, ACTUAL_HOURS=sum(sum(DATEPART(hh,[Time_Taken_in_Min]))+sum(DATEPART(MINUTE,[Time_Taken_in_Min])) /60)
FROM [HR_Admin].[dbo].[Mst_Daily_Report_Pds]
where date between '12-01-2016' and '12-14-2016'
and (DATENAME(weekday,[Date])) not in ('Sunday')
GROUP BY Name, [Date]
) a
GROUP BY Name
order by Actual_Hours desc

Last day of the month with a twist in SQLPLUS

I would appreciate a little expert help please.
in an SQL SELECT statement I am trying to get the last day with data per month for the last year.
Example, I am easily able to get the last day of each month and join that to my data table, but the problem is, if the last day of the month does not have data, then there is no returned data. What I need is for the SELECT to return the last day with data for the month.
This is probably easy to do, but to be honest, my brain fart is starting to hurt.
I've attached the select below that works for returning the data for only the last day of the month for the last 12 months.
Thanks in advance for your help!
SELECT fd.cust_id,fd.server_name,fd.instance_name,
TRUNC(fd.coll_date) AS coll_date,fd.column_name
FROM super_table fd,
(SELECT TRUNC(daterange,'MM')-1 first_of_month
FROM (
select TRUNC(sysdate-365,'MM') + level as DateRange
from dual
connect by level<=365)
GROUP BY TRUNC(daterange,'MM')) fom
WHERE fd.cust_id = :CUST_ID
AND fd.coll_date > SYSDATE-400
AND TRUNC(fd.coll_date) = fom.first_of_month
GROUP BY fd.cust_id,fd.server_name,fd.instance_name,
TRUNC(fd.coll_date),fd.column_name
ORDER BY fd.server_name,fd.instance_name,TRUNC(fd.coll_date)
You probably need to group your data so that each month's data is in the group, and then within the group select the maximum date present. The sub-query might be:
SELECT MAX(coll_date) AS last_day_of_month
FROM Super_Table AS fd
GROUP BY YEAR(coll_date) * 100 + MONTH(coll_date);
This presumes that the functions YEAR() and MONTH() exist to extract the year and month from a date as an integer value. Clearly, this doesn't constrain the range of dates - you can do that, too. If you don't have the functions in Oracle, then you do some sort of manipulation to get the equivalent result.
Using information from Rhose (thanks):
SELECT MAX(coll_date) AS last_day_of_month
FROM Super_Table AS fd
GROUP BY TO_CHAR(coll_date, 'YYYYMM');
This achieves the same net result, putting all dates from the same calendar month into a group and then determining the maximum value present within that group.
Here's another approach, if ANSI row_number() is supported:
with RevDayRanked(itemDate,rn) as (
select
cast(coll_date as date),
row_number() over (
partition by datediff(month,coll_date,'2000-01-01') -- rewrite datediff as needed for your platform
order by coll_date desc
)
from super_table
)
select itemDate
from RevDayRanked
where rn = 1;
Rows numbered 1 will be nondeterministically chosen among rows on the last active date of the month, so you don't need distinct. If you want information out of the table for all rows on these dates, use rank() over days instead of row_number() over coll_date values, so a value of 1 appears for any row on the last active date of the month, and select the additional columns you need:
with RevDayRanked(cust_id, server_name, coll_date, rk) as (
select
cust_id, server_name, coll_date,
rank() over (
partition by datediff(month,coll_date,'2000-01-01')
order by cast(coll_date as date) desc
)
from super_table
)
select cust_id, server_name, coll_date
from RevDayRanked
where rk = 1;
If row_number() and rank() aren't supported, another approach is this (for the second query above). Select all rows from your table for which there's no row in the table from a later day in the same month.
select
cust_id, server_name, coll_date
from super_table as ST1
where not exists (
select *
from super_table as ST2
where datediff(month,ST1.coll_date,ST2.coll_date) = 0
and cast(ST2.coll_date as date) > cast(ST1.coll_date as date)
)
If you have to do this kind of thing a lot, see if you can create an index over computed columns that hold cast(coll_date as date) and a month indicator like datediff(month,'2001-01-01',coll_date). That'll make more of the predicates SARGs.
Putting the above pieces together, would something like this work for you?
SELECT fd.cust_id,
fd.server_name,
fd.instance_name,
TRUNC(fd.coll_date) AS coll_date,
fd.column_name
FROM super_table fd,
WHERE fd.cust_id = :CUST_ID
AND TRUNC(fd.coll_date) IN (
SELECT MAX(TRUNC(coll_date))
FROM super_table
WHERE coll_date > SYSDATE - 400
AND cust_id = :CUST_ID
GROUP BY TO_CHAR(coll_date,'YYYYMM')
)
GROUP BY fd.cust_id,fd.server_name,fd.instance_name,TRUNC(fd.coll_date),fd.column_name
ORDER BY fd.server_name,fd.instance_name,TRUNC(fd.coll_date)