Related
I need to get the date and day count values between two dates.
let's suppose, we want to get the records between 1 july to 5 August.
the output should be like below Table image:
we already know that we have 5 weeks in july month and 1 week in august month for this date range :
let's start with first week to last week :
Try this Goutam Singh. I think daycount should now be 7, 2 using the monday as start of the week.
SELECT
[DYear],
[DMonth],
[Week],
DayCount=COUNT(DISTINCT DayCount),
BillableHour=SUM(BillableHour)
FROM
(
SELECT
[DYear]=(YEAR(Workdate)) ,
[DMonth]=(DATENAME(MONTH, Workdate)) ,
DateNames=datename(dw, Workdate),
[Week]='Week ' + CAST((DATEPART(wk,DATEADD(DAY, -1,Workdate)) - MAX(DATEPART(wk,DATEADD(DAY, -1,Workdate)) )over(partition by (select null))+2) AS varchar(20)),
DayCount= ( WorkDate),
BillableHour=(Convert(DECIMAL(16,2),[Hours]))
FROM
#TempTable
WHERE
Workdate between CONVERT(datetime,#FromDate) and CONVERT(datetime,#ToDate)
)G
GROUP BY
[DYear],
[DMonth],
[Week]
Number of days in a week column is not clear to me. otherwise, below will be the query. Just replace value and date columns with your appropriate column names.
select count(value),month(date),datepart(WEEKDAY,date()) as number of week,date
from t
group by date
#Goutam Singh I have here an updated version. Basically, you need a CTE to build a template table for your query and then do a join depending on what is in the table you want to get the TotalHours. Let me know if that is what you want.
DECLARE #StartDate DATE='20180101'
DECLARE #EndDate DATE='20180901'
DECLARE #Dates TABLE(
Workdate DATE Primary Key
)
DECLARE #TempTable TABLE (Id INT, Hours real, WorkDate DATETIME )
INSERT INTO #TempTable
SELECT 1, 5, '03.05.2018 00:00:00' UNION ALL
SELECT 2, 1.5, '08.05.2018 00:00:00' UNION ALL
SELECT 3, 3, '01.05.2018 00:00:00' UNION ALL
SELECT 4, 0, '04.05.2018 00:00:00' UNION ALL
SELECT 5, 2, '03.05.2018 00:00:00' UNION ALL
SELECT 6, 4, '03.05.2018 00:00:00' UNION ALL
SELECT 7, 2, '05.05.2018 00:00:00' UNION ALL
SELECT 8, 0.5, '08.05.2018 00:00:00' UNION ALL
SELECT 9, 0, '01.05.2018 00:00:00' UNION ALL
SELECT 10, 6, '08.05.2018 00:00:00' UNION ALL
SELECT 11, 8, '02.05.2018 00:00:00' UNION ALL
SELECT 12, 3.5, '09.05.2018 00:00:00' UNION ALL
SELECT 13, 1, '09.05.2018 00:00:00' UNION ALL
SELECT 14, 4, '04.05.2018 00:00:00' UNION ALL
SELECT 15, 1, '03.05.2018 00:00:00' UNION ALL
SELECT 16, 0, '02.05.2018 00:00:00' UNION ALL
SELECT 17, 3, '05.05.2018 00:00:00' UNION ALL
SELECT 18, 0.5, '04.05.2018 00:00:00' UNION ALL
SELECT 19, 2, '09.05.2018 00:00:00' UNION ALL
SELECT 20, 0, '09.05.2018 00:00:00'
--DATEADD(DAY, -1,Workdate)
;WITH Dates AS(
SELECT Workdate=#StartDate,WorkMonth=DATENAME(MONTH,#StartDate),WorkYear=YEAR(#StartDate), WorkWeek=datename(wk, DateAdd(DAY,-1,#StartDate) )
UNION ALL
SELECT CurrDate=DateAdd(DAY,1,Workdate),WorkMonth=DATENAME(MONTH,DateAdd(DAY,1,Workdate)),YEAR(DateAdd(DAY,1,Workdate)),datename(wk, Workdate) FROM Dates D WHERE Workdate<#EndDate ---AND (DATENAME(MONTH,D.Workdate))=(DATENAME(MONTH,D.Workdate))
)
SELECT
WorkMonth,
NumWeek=ROW_NUMBER()OVER(PARTITION BY WorkMonth+cast(WorkYear as varchar(20)) ORDER BY WorkdateStart),
NumDayWeek,
WorkYear,
WorkdateStart,
WorkdateEnd,
TotalHours=SUM(TotalHours)
FROM
(
SELECT
D.Workdate,
D.WorkMonth,
D.WorkYear,
D.WorkWeek,
WorkdateStart=MIN(D.Workdate) over(partition by cast(WorkWeek as varchar(20))+workmonth+cast(WorkYear as varchar(20))),
WorkdateEnd=MAX(D.Workdate) over(partition by cast(WorkWeek as varchar(20))+workmonth+cast(WorkYear as varchar(20))),
NumDayWeek=datediff(day,MIN(D.Workdate) over(partition by cast(D.WorkWeek as varchar(20))+workmonth+cast(WorkYear as varchar(20))),MAX(D.Workdate) over(partition by cast(D.WorkWeek as varchar(20))+workmonth+cast(WorkYear as varchar(20))))+1,
T.TotalHours,
T.DayCount
FROM
Dates D
LEFT JOIN
(
SELECT T.WorkDate, TotalHours=sum(T.Hours), DayCount=sum(case when T.Hours>0 then 1 else 0 end) FROM
#TempTable T
GROUP BY
T.WorkDate
)T ON
T.WorkDate = D.Workdate
)Sub
GROUP BY
WorkMonth,
WorkYear,
WorkdateStart,
NumDayWeek,
WorkdateEnd
ORDER BY
WorkdateStart
option (maxrecursion 0)
I have a flat file that I am importing into a SQL Server 2005 staging table as character data.
I need to convert the birthday field to datetime format when copying it to the final destination table. I was doing so using the following:
BIRTHDAY = case when isdate(DOB)=1 then convert(datetime, '19'+right(DOB, 2)+left(DOB, 2)+substring(DOB,3,2)) else null end
The problem is only 100+ of the birthdays from the 32k+ file are identified as dates.
I cannot see a difference between the ones that are dates and the ones that aren't. I have included a sampling below.
good date bad date
41129 100465
10531 122467
10429 20252
81030 62661
31231 20959
11028 91965
80928 60665
Looks like the raw data is in MMDDYY, but the months are not 0-padded.
Building on this assumption, you can parse the date parts like below and rebuild a datetime:
declare #raw table (dob varchar(100));
insert into #raw
select '41129' union all
select '10531' union all
select '10429' union all
select '81030' union all
select '31231' union all
select '11028' union all
select '80928' union all
select '100465' union all
select '122467' union all
select '20252' union all
select '62661' union all
select '20959' union all
select '91965' union all
select '60665'
select *,
[asDate] = dateadd(day, dd - 1, dateadd(month, mm - 1, dateadd(year, ('19' + yy)-1900, 0)))
from ( select dob,
substring(right('0' + dob, 6), 1, 2),
substring(right('0' + dob, 6), 3, 2),
substring(right('0' + dob, 6), 5, 2)
from #raw
) as stage (string, mm, dd, yy);
I need help writing a aging report on oracle.
The report should be like:
aging file to submit total 17
aging file to submit 0-2 days 3
aging file to submit 2-4 days 4
aging file to submit 4-6 days 4
aging file to submit 6-8 days 2
aging file to submit 8-10 days 4
I can create a query for each section and then union all the the results like:
select 'aging file to submit total ' || count(*) from FILES_TO_SUBMIT where trunc(DUE_DATE) > trunc(sysdate) -10
union all
select 'aging file to submit 0-2 days ' || count(*) from FILES_TO_SUBMIT where trunc(DUE_DATE) <= trunc(sysdate) and trunc(DUE_DATE) >= trunc(sysdate-2)
union all
select 'aging file to submit 2-4 days ' || count(*) from FILES_TO_SUBMIT where trunc(DUE_DATE) <= trunc(sysdate-2) and trunc(DUE_DATE) >= trunc(sysdate-4) ;
I was wondering if there is a better way using oracle analytic functions or any other query that will get better performance?
Sample data:
CREATE TABLE files_to_submit(file_id int, file_name varchar(255),due_date date);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 1, 'file_' || 1, sysdate);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 2, 'file_' || 2, sysdate -5);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 3, 'file_' || 3, sysdate -4);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 4, 'file_' || 4, sysdate);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 5, 'file_' || 5, sysdate-3);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 6, 'file_' || 6, sysdate-7);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 7, 'file_' || 7, sysdate-10);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 8, 'file_' || 8, sysdate-12);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 9, 'file_' || 9, sysdate-3);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 10, 'file_' || 10, sysdate-5);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 11, 'file_' || 11, sysdate-6);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 12, 'file_' || 12, sysdate-7);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 13, 'file_' || 13, sysdate-5);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 14, 'file_' || 14, sysdate-4);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 15, 'file_' || 15, sysdate-2);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 16, 'file_' || 16, sysdate-6);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 17, 'file_' || 17, sysdate-6);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 18, 'file_' || 18, sysdate-5);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 19, 'file_' || 19, sysdate-10);
INSERT INTO FILES_TO_SUBMIT(FILE_ID,FILE_NAME,DUE_DATE) VALUES ( 20, 'file_' || 20, sysdate-9);
DROP TABLE files_to_submit;
you can use this simple approach to get the report for all days(without total):
select
'aging file to submit '|| trunc(dist/2)*2 ||'-'|| (trunc(dist/2)*2+2) || ' days: ' || count(*)
from (
select trunc(sysdate) - trunc(DUE_DATE) as dist
from FILES_TO_SUBMIT
--where trunc(DUE_DATE) > trunc(sysdate) -10
)
group by trunc(dist/2)
order by trunc(dist/2);
The only thing that is important is just number of days (dist(ance) field).
If you want to have also the Total in the same scan:
select
'aging file to submit '||
case
when trunc(dist/2) is null
then 'Total '
else trunc(dist/2)*2 ||'-'|| (trunc(dist/2)*2+2) || ' days: '
end ||
count(*)
from (
select trunc(sysdate) - trunc(DUE_DATE) as dist
from FILES_TO_SUBMIT
where trunc(DUE_DATE) > trunc(sysdate) -10
)
group by rollup(trunc(dist/2))
order by trunc(dist/2)
nulls first;
Hint: If you have hundreds of days of history an index would be useful. (pay attention: if your table is very big, >100Milion, the creation of the index will take some time)
create index index_name on files_to_submit(due_date);
and then change the condition to:
where DUE_DATE > trunc(sysdate) - 10
This will speed up y
Allow me to suggest WIDTH_BUCKET.
This will divide the date range into equal size. Since you want 10 days range into groups of 2 days, the bucket size will be 10 / 2 = 5.
Query:
SELECT
CASE GROUPING(bucket)
WHEN 1
THEN 'aging file to submit Total'
ELSE 'aging file to submit ' || (bucket-1)*2 || '-' || (bucket)*2 || ' days'
END AS bucket_number,
COUNT(1) AS files
FROM (
SELECT
WIDTH_BUCKET(due_date, sysdate, sysdate-10, 5) bucket
FROM
files_to_submit
WHERE
due_date >= sysdate-10
)
GROUP BY
ROLLUP(bucket)
ORDER BY
bucket NULLS FIRST;
Result:
BUCKET_NUMBER FILES
------------------------------------ ----------
aging file to submit Total 17
aging file to submit 0-2 days 2
aging file to submit 2-4 days 3
aging file to submit 4-6 days 6
aging file to submit 6-8 days 5
aging file to submit 8-10 days 1
I got different counts using your sample data -- I get 19 total instead of 17 (which seems appropriate as only one of the 20 records in your sample data is out of the range):
WITH d1 AS (
SELECT 2 AS day_cnt FROM dual
UNION ALL
SELECT 4 FROM dual
UNION ALL
SELECT 6 FROM dual
UNION ALL
SELECT 8 FROM dual
UNION ALL
SELECT 10 FROM dual
)
SELECT NVL(title, 'aging file to submit total') AS title, COUNT(DISTINCT file_id)
FROM (
SELECT 'aging file to submit ' || prev_day || '-' || day_cnt || ' days' AS title, f1.file_id
FROM (
SELECT day_cnt, NVL(LAG(day_cnt) OVER ( ORDER BY day_cnt ), 0) AS prev_day
FROM d1
) d2, files_to_submit f1
WHERE TRUNC(f1.due_date) <= TRUNC(SYSDATE - d2.prev_day)
AND TRUNC(f1.due_date) >= TRUNC(SYSDATE - d2.day_cnt)
) GROUP BY ROLLUP(title);
Also, the counts for the day ranges aren't right (they don't add up to 19, that is) because of the the files can be counted twice due to the use of TRUNC() and including both end cases. But I'm sure you can tweak the above to give what you want.
WITH r (
'aging file to submit ' Prefix,
Total,
Days0_2,
Days2_4,
Days4_6,
Days6_8,
Days8_10
) AS (
SELECT
SUM(Total) Total,
SUM(Days0_2) Days0_2,
SUM(Days2_4) Days2_4,
SUM(Days4_6) Days4_6,
SUM(Days6_8) Days6_8,
SUM(Days8_10) Days8_10
FROM (
SELECT
(CASE WHEN f.days <= 2 THEN f.num ELSE NULL END) AS Days0_2,
(CASE WHEN f.days >= 2 AND f.days <= 4 THEN f.num ELSE NULL END) AS Days2_4,
(CASE WHEN f.days >= 4 AND f.days <= 6 THEN f.num ELSE NULL END) Days4_6,
(CASE WHEN f.days >= 6 AND f.days <= 8 THEN f.num ELSE NULL END) AS Days6_8,
(CASE WHEN f.days >= 8 AND f.days <= 10 THEN f.num ELSE NULL END) AS Days8_10,
f.num AS Total
FROM (
SELECT
COUNT(*) AS num,
TRUNC(due_date) - TRUNC(SYSDATE) + 10 AS days
FROM FILES_TO_SUBMIT t
WHERE (TRUNC(due_date) - TRUNC(SYSDATE) + 10) >= 0
GROUP BY TRUNC(due_date) - TRUNC(SYSDATE) + 10
) f
) s
)
SELECT Prefix || 'Total' AS Label, Total AS Count FROM r
UNION ALL SELECT Prefix || '0-2 days', Days0_2 FROM r
UNION ALL SELECT Prefix || '2-4 days', Days2_4 FROM r
UNION ALL SELECT Prefix || '4-6 days', Days4_6 FROM r
UNION ALL SELECT Prefix || '6-8 days', Days6_8 FROM r
UNION ALL SELECT Prefix || '8-10 days', Days8_10 FROM r
It will not double count records for the Total row. Since your day ranges overlap you cannot sum up the individual counts to get the total. As another query given here gives a total of 25 with only 20 records and 1 out of range.
The result for total is what you expect with 20 records and 1 being 12 days old. The innermost query does all the heavy lifting but it is executed once to get all aging results. Its result will be at most 11 rows, 0-10 days. The rest of the queries are for final results and pretty output.
You can eliminate one query level by SUMing one level in, I just find it easier to validate the results by being able to select intermediate queries for a spot check.
Here is the result of the query:
This approach allows you to maintain your buckets separately from your main SQL in case you want them to be different sizes or to name them something that is not generated from the SQL, such as 'On time', 'Delinquent', etc. and also provides a very readable main SQL block.
with aging as
(select count(*) count_per_day, (trunc(sysdate) - trunc(f.due_date)) age
from files_to_submit f
where trunc(f.due_date - 10) <= sysdate
group by (trunc(sysdate) - trunc(f.due_date))),
buckets as
(select 1 bucket_id, 0 bucket_min, 2 bucket_max, 'aging file to submit 0-2' bucket_name from dual
union select 2, 2, 4, 'aging file to submit 2-4' from dual
union select 3, 4, 6, 'aging file to submit 4-6' from dual
union select 4, 6, 8, 'aging file to submit 6-8' from dual
union select 5, 8, 10, 'aging file to submit 8-10' from dual
union select 6, null, null, 'aging file to submit total' from dual
)
select nvl(b.bucket_name, (select bucket_name from buckets where bucket_id = 6)), sum(a.count_per_day) bucket_cnt
from aging a
join buckets b on (a.age >= b.bucket_min and a.age <= b.bucket_max)
group by rollup(b.bucket_name)
order by b.bucket_name nulls first;
I want to make a SQL query that gets todays date and the most recent date from a date column. So if I have three records in my database that have the following dates:
March 8, 2012
March 2, 2012
December 8, 2011
I want the SQL query to return all records for March 8, 2012 and March 2, 2012 (most recent date). How can I do this?
I can date today's date using:
CONVERT( varchar(100), DATEADD( DAY, 0, getdate() ), 111)
Thank You
Edit:
Thanks everyone. I just have one more question. I have created two views:
create view with top dates
CREATE VIEW topDates AS
select DISTINCT TOP 3 replace(CONVERT(VARCHAR(20),date,111),'-','/') AS dates from CSAResults.dbo.Details
create view dateTwo
select *
from (select ROW_NUMBER() over (order by dates desc) as srNo, dates
from topDates)
AS employee
WHERE srNo=2
And now I want to select * from my DB where a column is equal to the 'dates' column from the view 'dateTwo'
select buildNumber
from CSAResults.dbo.Details
where buildNumber LIKE '%Main '+ (SELECT dates FROM dateTwo) + '%'
But this returns nothing.
Thanks
You can do the following:
select date
from yourtable
where
(
date = Convert(varchar(10), getdate(), 101)
OR
date IN (SELECT Max(date)
FROM yourtable
WHERE date!= Convert(varchar(10), getdate(), 101))
)
Here is an example script that does what you are asking. It uses a sub-query to select all records with MAX on the date. You would just add an OR to also select items for the current date.
DECLARE #A TABLE
(
part_no VARCHAR(5),
rev CHAR,
on_hand TINYINT,
safety_stock TINYINT,
so_no VARCHAR(5),
so_date DATETIME
)
INSERT #A
SELECT '12345', 'A', 10, 15, 'S1234', '12/14/2009' UNION ALL
SELECT '12345', 'A', 10, 15, 'S1233', '10/01/2009' UNION ALL
SELECT '12345', 'A', 10, 15, 'S1232', '08/02/2009' UNION ALL
SELECT '12346', '', 5, 0, 'S1231', '08/01/2009' UNION ALL
SELECT '12347', '-', 0, 0, 'S1230', '10/20/2009' UNION ALL
SELECT '12347', '-', 0, 0, 'S1229', '07/15/2009'
SELECT * FROM #A AS A
WHERE so_date =
(
SELECT MAX(so_date)
FROM #A AS B
WHERE B.part_no = A.part_no AND B.Rev = A.Rev
)
SELECT *
INTO #TEMP
FROM
(
SELECT GETDATE() DATE_FIELD, 'Blah1...' OTHER_FIELDS
UNION SELECT GETDATE() DATE_FIELD, 'Blah2...' OTHER_FIELDS
UNION SELECT DATEADD(d,-1,GETDATE()) DATE_FIELD, 'Blah3...' OTHER_FIELDS
UNION SELECT DATEADD(d,-1,GETDATE()) DATE_FIELD, 'Blah4...' OTHER_FIELDS
UNION SELECT DATEADD(d,-3,GETDATE()) DATE_FIELD, 'Blah5...' OTHER_FIELDS
) A
SELECT * FROM #TEMP
SELECT * FROM
(
SELECT DATE_FIELD, OTHER_FIELDS,
DENSE_RANK() OVER (ORDER BY DATE_FIELD DESC) _RANK
FROM #TEMP
) A
WHERE A._RANK < 3
For your second question:
select buildNumber
from CSAResults.dbo.Details
inner join dateTwo
on buildNumber LIKE '%Main '+ dateTwo.dates + '%'
The Situation (Ignore this it is boring):
I have reports that I created using reporting services. Some of these reports take the parameter, "Month". They enter in an integer for the month they want. Example: December = 12.
In order to view the report, I am simply using the Report Viewer in visual studio. I need the month field to be a drop down box in order to select the month by name. There is a feature in in reporting services that allows you to bind the field to a stored procedure in order to create value/text pairs for the drop down list.
The Problem:
I don't want to create a "months" table in my database but I need to have a stored procedure that can return all the month/int pairs. I'm sure there is a very easy solution to this but I'm not sure what it is! My first thought was creating a temp table, but I am not sure how to add manually add each month/int pair to the table... All your suggestions are appreciated!
What I want is the following statement, except without the use of the Months Table:
SELECT MonthID, MonthName
FROM Months
How about:
CREATE PROCEDURE ListMonths
AS
SELECT 1 AS MonthId, 'January' AS MonthName
union all select 2, 'February'
union all select 3, 'March'
union all select 4, 'April'
union all select 5, 'May'
union all select 6, 'June'
union all select 7, 'July'
union all select 8, 'August'
union all select 9, 'September'
union all select 10, 'October'
union all select 11, 'November'
union all select 12, 'December'
GO
Call this and I believe it returns what you want.
Do you really need to create a table for this? You could just do the selects manually:
SELECT 1, 'January' UNION ALL
SELECT 2, 'February' UNION ALL
...
SELECT 12, 'December'
Sounds like what you need is a view, basically you write a custom query and it returns it as a table.
Also, a SP can just contain a Query and it will return a table, just create it as:
CREATE PROCEDURE [dbo].[mytable]
-- Add the parameters for the stored procedure here
#inputarg1 = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT
.....
FROM
.....
WHERE
.....
END
in the stored proc, do the following
Declare #Months Table
(monthNo TinyInt Primary Key Not Null,
name varChar(10) Not Null,
Abbrev char(3) = Substring(name,1,3))
Insert #Months ( monthNo, name) Values (1, 'January')
Insert #Months ( monthNo, name) Values (2, 'February')
...
Insert #Months ( monthNo, name) Values (12, 'December')
or, if you need this table in more than one stored proc, write a UDF that dies the same thing and returns this table to the calling proc...
WITH months AS
(
SELECT CAST('2009.01.01' AS DATETIME) AS m, 1 AS num
UNION ALL
SELECT DATEADD(month, 1, m), num + 1
FROM months
WHERE num < 12
)
SELECT num, DATENAME(month, m)
FROM months