I have a table named Leave it contains 2 rows
no=1 Leave=CL,Lnumber=2,FromDate='2015-01-10',ToDate=2015-01-11'
no=2 Leave=CL,Lnumber=2,FromDate='2015-01-12',ToDate=2015-01-13'
no=3 Leave=CL,Lnumber=2,FromDate='2015-01-15',ToDate='2015-02-16'
no=4 Leave=CL,Lnumber=2,FromDate='2015-01-31',ToDate='2015-02-01'
Here I want to get january month leave report(leave and Lnumer). How can I fetch this considering this FromDate and ToDate
The answer should be like this
Leave=CL,Lnumber=7 (for jan month)
Leave=Cl,Lnumber=1 (for feb month)
I am using MSSQL 2008. You can add extra fields if it is necessory. Thank You
I'd recommend building a calendar table (basically a tally table, but with dates). Then join your leave table to the calendar table between the from and to dates, and count the days that fall in the interval.
;with t as
(
select no=1, Leave='CL',Lnumber=2,FromDate=cast('2015-01-10' as date),ToDate=cast('2015-01-11' as date) union all
select no=2, Leave='CL',Lnumber=2,FromDate=cast('2015-01-31' as date),ToDate=cast('2015-02-01' as date)
), cal as
(
select top 10000 _date = cast(cast(row_number() over (order by (select null)) + 41000 as datetime) as date)
from sys.objects a, sys.objects b
)
select
t.Leave,
_year = datepart(year, c._date),
_month = datepart(month, c._date),
count(_date)
from t
inner join cal c
on c._date between t.FromDate and t.ToDate
group by t.Leave, datepart(year, c._date), datepart(month, c._date)
Related
I need help. We need to insert data between two dates in the database using C# on button click
For example, we have two calendars, Calendar1 & Calendar2.
Now from Calendar1, we choose date 01/12/2018
And from Calendar2, we choose date 30/12/2018
When we click the "Submit" button, we need to insert the data into the database
01/12/2018 to 30/12/2018
Ex.
01/12/2018
02/12/2018
03/12/2018
You can generate a table between two dates using recursive CTEs:
with dates as (
select #startdate as dte
union all
select dateadd(day, 1, dte)
from dates
where dte < #enddate
)
select *
from dates
option (maxrecursion 0);
That said, if you need to do this for a user interface, then I think you really need a calendar table, with one row per date. Then you can simply do:
select c.dte
from calendar c
where c.dte >= #startdate and c.dte <= #enddate;
I personally prefer a Tally Table to an rCTE for things like this; for larger data sets a Tally Table tends to be much faster:
DECLARE #StartDate date = '20181201',
#EndDate date = '20181231';
WITH N AS (
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1 AS I
FROM N N1, N N2, N N3, N N4) --10000 rows
SELECT DATEADD(DAY, T.I, #StartDate)
FROM Tally T
WHERE DATEADD(DAY, T.I, #StartDate) <= #EndDate;
In a month, seperate by AccountNo, all transaction that exceeds 10,000 amount within 3 days must be outputted.
This is a sample table:
AccountNo-----Date------------Amount
1-------------5/1/17----------8000
1-------------5/3/17----------1000
1-------------5/4/17----------1000
1-------------5/6/17----------1000
2-------------5/7/17----------3000
2-------------5/10/17---------2000
2-------------5/13/17---------2000
2-------------5/13/17---------3000
3-------------5/14/17---------3000
3-------------5/15/17---------3000
3-------------5/16/17---------9000
4-------------5/17/17---------1000
5-------------5/18/17---------1000
5-------------5/19/17---------1000
5-------------5/20/17---------1000
The result must be:
AccountNo-----Date------------Amount
1-------------5/1/17----------8000
1-------------5/3/17----------1000
1-------------5/4/17----------1000
2-------------5/7/17----------3000
2-------------5/10/17---------2000
2-------------5/13/17---------2000
2-------------5/13/17---------3000
3-------------5/14/17---------3000
3-------------5/15/17---------3000
3-------------5/16/17---------9000
There is a code given to me, but it's not completely working yet.
Select A.*
From YourTable A
Cross Apply (
Select RT1 = sum(case when [Date] <= B2.TstDate then [Amount] else 0 end)
,RT2 = sum(case when [Date] >= B2.TstDate then [Amount] else 0 end)
From YourTable B1
Cross Join (Select TstDate=A.[Date]) B2
Where [Date] between DateAdd(DAY,-2,A.[Date]) and DateAdd(DAY,2,A.[Date])
and Year([Date])=Year(TstDate)
and Month([Date])=Month(TstDate)
) B
Where RT1>=10000 or RT2>=10000
Common table expression: TransInfo
TransInfo is a table with account, year, month, day, amount
The main query:
The [Tmiddle] query adds a row_number based on amount per day per month per accountno
The [Tmiddle] is joined again with TransInfo to limit the results to top 3 of a month AS [Touter] where the top 3 is >= 10.000
The outer most query combines the result with a table again to gain the complete transaction information again
In short:
Filter data on top 3 amounts per day per month per year per account
Check if sum of amount >= 10.000
Show results
Query:
WITH [TransInfo] ([AccountNo], [Year], [Month], [Day], [Amount], [Rownumber])
AS
(
SELECT [AccountNo]
,[Year]
,[Month]
,[Day]
,[Amount]
,ROW_NUMBER() OVER
(
PARTITION BY [AccountNo],
[Year],
[Month]
ORDER BY [Amount] DESC
) AS [Rownumber]
FROM
(
SELECT [AccountNo]
,DATEPART(YEAR, [Date]) AS [Year]
,DATEPART(MONTH, [Date]) AS [Month]
,DATEPART(DAY, [Date]) AS [Day]
,SUM([Amount]) AS [Amount]
FROM [Test].[dbo].[Data]
GROUP BY [AccountNo],
DATEPART(MONTH, [Date]),
DATEPART(YEAR, [Date]),
DATEPART(DAY, [Date])
) AS [Tinner]
)
SELECT [Data].[AccountNo]
,[Data].[Date]
,[Data].[Amount]
FROM [Test].[dbo].[Data]
INNER JOIN
(
SELECT [TransInfo].[AccountNo]
,[TransInfo].[Year]
,[TransInfo].[Month]
,[TransInfo].[Day]
,[TransInfo].[Amount]
FROM [TransInfo]
INNER JOIN
(
SELECT [AccountNo]
,[Year]
,[Month]
FROM [TransInfo]
WHERE [TransInfo].[Rownumber] <= 3
GROUP BY [TransInfo].[AccountNo],
[TransInfo].[Year],
[TransInfo].[Month]
HAVING SUM ([TransInfo].[Amount]) >= 10000
) AS [Tmiddle]
ON [Tmiddle].[AccountNo] = [TransInfo].[AccountNo]
AND [Tmiddle].[Year] = [TransInfo].[Year]
AND [Tmiddle].[Month] = [TransInfo].[Month]
WHERE [TransInfo].[Rownumber] <= 3
) AS [Touter]
ON [Data].[AccountNo] = [TOuter].[AccountNo]
AND DATEPART(YEAR, [Data].[Date]) = [TOuter].[Year]
AND DATEPART(MONTH, [Data].[Date]) = [TOuter].[Month]
AND DATEPART(DAY, [Data].[Date]) = [TOuter].[Day]
Result: Left is query result, right is complete table
Does this work as expected? You may need to change the 2's to 3's depending on what you mean by 'within 3 days'.
SELECT DISTINCT C.*
FROM YourTable C
INNER JOIN (
SELECT a.AccountNo, a.Date
FROM YourTable a
INNER JOIN YourTable b
ON a.AccountNo = b.AccountNo
AND DATEADD(DAY, 2, a.Date) <= b.Date AND b.Date >= a.Date
GROUP BY a.AccountNo, a.Date
HAVING SUM(b.Amount) > 10000
) d
ON C.AccountNo = d.AccountNo
AND DATEADD(DAY, 2, D.Date) <= C.Date AND C.Date >= D.Date
I have a table that looks like this:
timestamp | commodity | amount | price |
I am trying to get the weighted average of a commodity ,for each day from the last 7 days(In order to create a graph, c# program), I managed to create a query to get the weighted average:
SELECT SUM(price * amount),
SUM(amount)
FROM [table_name]
WHERE (commodity = #commodity)
AND (timestamp >= #fromDate
AND timestamp <= #toDate);
So, I send this query 7 times, each time adding (-1) to the fromDate and toDate, to get the averages of last 7 days.
I would like to ask if there is a way to do this in 1 query, and if there is, if it can be implemented to get the same information of the last month.
you can use convert to date and do code as below:
SELECT convert(date, timestamp) as [Dates],
SUM(price * amount),
SUM(amount)
FROM [table_name]
WHERE (commodity = #commodity)
AND (timestamp >= #fromDate
AND timestamp <= #toDate)
GROUP BY convert(date, timestamp)
you have to group by date
SELECT SUM(price* amount),SUM(amount) FROM [table_name] WHERE (commodity = #commodity) AND (cast(timestamp as date)>= #fromDate AND cast(timestamp as date)<= #toDate)
Group By cast(timestamp as date)
You should to group your data by date:
SELECT SUM(price * amount),SUM(amount)
FROM [table_name]
WHERE (commodity = #commodity)
AND (timestamp >= #fromDate
AND timestamp <= #toDate)
GROUP BY CAST(timestamp as DATE);
To generate dates, you can use as below:
;with Datescte as (
Select top (datediff(day, #fromDate, #toDate) +1) Dt = Dateadd(day, Row_Number() over (order by (Select null))-1, #fromDate)
from master..spt_values s1, master..spt_values s2
), cte2 as (
SELECT convert(date, timestamp) as [Dates],
SUM(price * amount) as [Sum_Price_Amount],
SUM(amount) as [Sum_Amount]
FROM [table_name]
WHERE (commodity = #commodity)
AND (timestamp >= #fromDate
AND timestamp <= #toDate)
GROUP BY convert(date, timestamp)
)
Select Dt as [Dates] , Sum_Price_Amount, Sum_Amount
FROM Datescte d
LEFT JOIN cte2 d2
ON d.Dt = d2.Dates
I have a dataset where I have some daily observations. I want to group them by date and later take the average of the daily Sum per day (in SQL).
The SQL used to group by day looks like this and works.
"SELECT Date, Sum(Margin) FROM [OpenPositions] GROUP BY Date"
I have tried the following code to do the intended query in one SQL.
"SELECT Datepart(week, Date),
Avg(avgmargin)
FROM (SELECT Datepart(year, Date) Year,
Datepart(week, Date) Week,
Sum(Margin) sum
FROM [OpenPositions]
GROUP BY Datepart(year, Date),
Datepart(week, Date)"
But this does not work (not showing any data). Can anyone find the mistake in my sql query, and how to come around it?
All help appreciated.
Try this:
Select Datepart(week, Date), Avg(avgmargin) from (
SELECT Date, Sum(Margin) AS Margin FROM [OpenPositions] GROUP BY Date)
Group By Datepart(week, Date)
Edit #2
Try This:
Select TBL.week, Avg(TBL.margin) from (
SELECT Datepart(week, Date) as week ,Datepart(day, Date), Sum(Margin) AS margin
FROM [OpenPositions] GROUP BY Datepart(week, Date),Datepart(day, Date)) as TBL
Group By TBL.week
#calc16 in regard to your response bellow, why don't you do this:
Select TBL.week, Avg(TBL.margin) from
(SELECT Datepart(week, Date) as week, Date, Sum(Margin) AS margin FROM[OpenPositions]
GROUP BY Datepart(week, Date), Date) as TBL
Group By TBL.week
Edit #4
you should add the year to the group in the inner query.
Select TBL.week, , Avg(TBL.margin) from
(SELECT Datepart(week, Date) as week,Datepart(year, Date) as year, Date, Sum(Margin) AS margin FROM[OpenPositions]
GROUP BY Datepart(week, Date), Datepart(year, Date) ,Date) as TBL
Group By TBL.week, TBL.year
My data looks like below.
How can I find missing date range from ss table.
I want to find missing(ss date range) date range between se_startdate and se_enddate.
for example above.
Missing date ranges are
2014-07-01 to 2014-07-06
2014-07-18 to 2014-07-30.
There may be a simpler way to do this, but often when trying to find missing numbers/dates you need to create those numbers/dates then LEFT JOIN to your existing data to find what is missing. You can create the dates in question with a recursive cte:
WITH cal AS (SELECT CAST('2014-07-01' AS DATE) dt
UNION ALL
SELECT DATEADD(DAY,1,dt)
FROM cal
WHERE dt < '2014-07-30')
SELECT *
FROM cal
Then, you LEFT JOIN to your table to get a list of missing dates:
WITH cal AS (SELECT CAST('2014-07-01' AS DATE) dt
UNION ALL
SELECT DATEADD(DAY,1,dt)
FROM cal
WHERE dt < '2014-07-30')
SELECT DISTINCT cal.dt
FROM cal
LEFT JOIN YourTable a
ON cal.dt BETWEEN CAST(SS_StartDate AS DATE) AND CAST(SS_EndDate AS DATE)
WHERE a.SS_StartDate IS NULL
Then you need to find out whether or not consecutive rows belong in the same range, or if they have a gap between them, using DATEDIFF() and ROW_NUMBER():
WITH cal AS (SELECT CAST('2014-07-01' AS DATE) dt
UNION ALL
SELECT DATEADD(DAY,1,dt)
FROM cal
WHERE dt < '2014-07-30')
,dt_list AS (SELECT DISTINCT cal.dt
FROM cal
LEFT JOIN YourTable a
ON cal.dt BETWEEN CAST(SS_StartDate AS DATE) AND CAST(SS_EndDate AS DATE)
WHERE a.SS_StartDate IS NULL)
SELECT dt
,DATEDIFF(D, ROW_NUMBER() OVER(ORDER BY dt), dt) AS dt_range
FROM dt_list
Then use MIN() and MAX() to get the ranges:
WITH cal AS (SELECT CAST('2014-07-01' AS DATE) dt
UNION ALL
SELECT DATEADD(DAY,1,dt)
FROM cal
WHERE dt < '2014-07-30')
,dt_list AS (SELECT DISTINCT cal.dt
FROM cal
LEFT JOIN YourTable a
ON cal.dt BETWEEN CAST(SS_StartDate AS DATE) AND CAST(SS_EndDate AS DATE)
WHERE a.SS_StartDate IS NULL)
,dt_range AS (SELECT dt
,DATEDIFF(D, ROW_NUMBER() OVER(ORDER BY dt), dt) AS dt_range
FROM dt_list)
SELECT MIN(dt) AS BeginRange
,MAX(dt) AS EndRange
FROM dt_range
GROUP BY dt_range;
--OPTION (MAXRECURSION 0)
Demo: SQL Fiddle
Note: If the range you're checking is more than 100 days you'll need to specify the MAXRECURSION, 0 means no limit.
Note2: If your SE dates are intended to drive the complete date range, then change the cal cte from fixed dates to queries using MIN() and MAX() respectively.
A missing range must start at either se_StartDate or ss_EndDate+1. Likewise, it must end on either se_EndDate or ss_StartDate-1. Permute the candidate ranges and discard the overlappers.
The advantage of this method is that the time precision is easily adjustable to hours, or minutes or seconds without needing to enumerate every clock tick.
SQL Fiddle Demo
SELECT DISTINCT
range_start, range_end, se_StartDate, se_EndDate
FROM MyTable t1
CROSS APPLY (
SELECT se_StartDate range_start
UNION ALL
SELECT DATEADD(day,1,SS_EndDate)
) rs
CROSS APPLY (
SELECT se_EndDate range_end
UNION ALL
SELECT DATEADD(day,-1,SS_StartDate)
FROM MyTable
WHERE
se_StartDate = t1.se_StartDate AND
se_EndDate = t1.se_EndDate AND
SS_StartDate > range_start
) re
WHERE NOT EXISTS (
SELECT 1
FROM MyTable
WHERE
range_start < SS_EndDate AND
range_end > SS_StartDate
)