Adding data ordering by date in SQL - sql

How to add values separately by date in order. Suppose there're several values with same date with a particular field. I wamt to sum up those data and want to have a particular row with one date. oredered by date.

So let's say you have some data like this:
DATE VALUE
------------------------------
2017-01-01 100
2017-02-01 50
2017-01-01 75
2017-02-01 25
What it sounds like you want to do is take those 4 rows and combine them down to 2, one for 2017-01-01 and one for 2017-02-01, is that correct?
If so, you just need to do an INSERT with a SELECT and GROUP BY on the origin data.
So if I am trying to INSERT to Test_Table_One and my data was in Origin_Table_One, I would do:
INSERT INTO Test_Table_One
SELECT DATE, SUM(VALUE)
FROM Origin_Table_One
GROUP BY DATE
Does tht make sense?

Try this:
create table #temp
(
Date date,
Sum bigint
)
create table #tempVarious
(
Date date,
value bigint
)
insert into #tempVarious
values
(cast(getdate() as date), 1),
(cast(getdate() as date), 1),
(cast(getdate() as date), 3),
(cast(getdate() as date), 5),
(cast(getdate() as date), 6),
(cast(getdate() as date), 8),
(cast(getdate() as date), 3),
(cast(dateadd(day,-1,getdate()) as date), 1),
(cast(dateadd(day,-1,getdate()) as date), 3),
(cast(dateadd(day,-1,getdate()) as date), 6),
(cast(dateadd(day,-1,getdate()) as date), 2),
(cast(dateadd(day,-1,getdate()) as date), 1)
insert
into #temp
select date
, sum(value)
from #tempVarious
group by date
select *
from #temp

It seems you are very new in SQL, please read these two tutorials about GROUP BY https://www.w3schools.com/sql/sql_groupby.asp and ORDER BY https://www.w3schools.com/sql/sql_orderby.asp
SELECT DATE_COLUMN, SUM(VALUE_COLUM)
FROM TABLE
GROUP BY DATE_COLUMN
ORDER BY DATE_COLUMN
A variant in case you want to order by descending
ORDER BY DATE_COLUMN DESC
In case your column is of type DateTime and you have different times in a single date
SELECT CAST(DATE_COLUMN AS DATE) AS DC, SUM(VALUE_COLUM)
FROM TABLE
GROUP BY CAST(DATE_COLUMN AS DATE)
ORDER BY DC

Related

How to display months sorted in order in SQL Server?

Below is the table I have created and inserted values in it:
CREATE TABLE employees_list
(
employeeID int identity(1,1),
employeeName varchar(25)
)
GO
INSERT INTO employees_list VALUES ('Kevin'),('Charles')
GO
CREATE TABLE hourlyRates
(
employeeID int,
rate int,
rateDate date
)
INSERT INTO hourlyRates VALUES (1, 28, '2016-01-01'),
(1, 39, '2016-02-01'),
(2, 43, '2016-01-01'),
(2, 57, '2016-02-01')
CREATE TABLE workingHours
(
employeeID int,
startdate datetime,
enddate datetime
)
GO
INSERT INTO workingHours VALUES (1, '2016-01-01 09:00', '2016-01-01 17:00'),
(1, '2016-01-02 09:00', '2016-01-02 17:00'),
(1, '2016-02-01 10:00', '2016-02-01 16:00'),
(1, '2016-02-02 11:00', '2016-02-02 13:00'),
(2, '2016-01-01 10:00', '2016-01-01 16:00'),
(2, '2016-01-02 08:00', '2016-01-02 14:00'),
(2, '2016-02-01 14:00', '2016-02-01 19:00'),
(2, '2016-02-02 13:00', '2016-02-02 16:00')
GO
SELECT * FROM employees_list
SELECT * FROM hourlyRates
SELECT * FROM workingHours
Then I ran a query to calculate salaries paid to Employees each month:
SELECT
employeeName,
DATENAME(MONTH, startdate) AS 'Month',
SUM(DATEDIFF(HOUR, startdate, enddate) * rate) AS 'Total Salary'
FROM
hourlyRates, workingHours, employees_list
WHERE
hourlyRates.employeeID = workingHours.employeeID
AND employees_list.employeeID = workingHours.employeeID
AND (hourlyRates.rateDate BETWEEN DATEFROMPARTS(DATEPART(YEAR, workingHours.startDate), DATEPART(MONTH, workingHours.startDate),1)
AND DATEFROMPARTS(DATEPART(YEAR, workingHours.endDate), DATEPART(MONTH, workingHours.endDate),1))
GROUP BY
employeeName, DATENAME(MONTH, startdate)
And I got the following output:
As you can see from the screenshot above that I got the result I wanted.
But the only issue is the month is not being displayed in order.
I tried adding ORDER BY DATENAME(MONTH, startdate) and still the order of month is not being sorted.
I even tried ORDER BY DATEPART(MM, startdate) but it is showing error mentioning that it is not contained in an aggregate function or GROUP BY clause.
What minor change do I need to make in my query ?
Why add ORDER BY DATENAME(MONTH,startdate) not work
Because the ORDER depends on character instead of the month of number.
You can try to add MONTH(startdate) in ORDER BY & GROUP BY, because you might need to add non-aggregate function in GROUP BY
SELECT employeeName,DATENAME(MONTH,startdate) AS 'Month',
SUM(DATEDIFF(HOUR,startdate,enddate) * rate) AS 'Total Salary'
FROM hourlyRates
INNER JOIN workingHours
ON hourlyRates.employeeID = workingHours.employeeID
INNER JOIN employees_list
ON employees_list.employeeID = workingHours.employeeID
WHERE
(hourlyRates.rateDate
BETWEEN DATEFROMPARTS(DATEPART(YEAR, workingHours.startDate), DATEPART(MONTH,workingHours.startDate),1)
AND DATEFROMPARTS(DATEPART(YEAR, workingHours.endDate), DATEPART(MONTH,workingHours.endDate),1))
GROUP BY employeeName,DATENAME(MONTH,startdate),MONTH(startdate)
ORDER BY MONTH(startdate)
sqlfiddle
NOTE
I would use INNER JOIN ANSI syntax instead of , which mean CROSS JOIN because JOIN syntax is generally considered more readable.
As mentioned, ORDER BY DATENAME will sort by the textual name of the month not by the actual ordering of months.
It's best to just group and sort by EOMONTH, then you can pull out the month name from that in the SELECT
Further improvements:
Always use explicit join syntax, not old-style , comma joins.
Give tables short aliases, to make your query more readable.
Your date interval check might not be quite right, and you may need to also adjust the rate caluclation, but I don't know without further info.
A more accurate calculation would probably mean calculating part-dates.
SELECT
e.employeeName,
DATENAME(month, EOMONTH(wh.startdate)) AS Month,
SUM(DATEDIFF(HOUR, wh.startdate, wh.enddate) * hr.rate) AS [Total Salary]
FROM hourlyRates hr
JOIN workingHours wh ON hr.employeeID = wh.employeeID
AND hr.rateDate
BETWEEN DATEFROMPARTS(YEAR(wh.startDate), MONTH(wh.startDate), 1)
AND DATEFROMPARTS(YEAR(wh.endDate), MONTH(wh.endDate), 1)
JOIN employees_list e ON e.employeeID = wh.employeeID
GROUP BY
e.employeeId,
e.employeeName,
EOMONTH(wh.startdate)
ORDER BY
EOMONTH(wh.startdate),
e.employeeName;
db<>fiddle

Creating a table of sequences (integers and dates)

Sometimes I need to create tables of regularly occuring sequences of dates or integers. I manually create them and that works but it's more difficult to maintain because there's a lot of code duplication. What is the more maintainable way to say a sequence of integers or dates that occur at regular intervals?
Here's my current approach:
DECLARE #IndexDate TABLE (
[Id] INT,
[Date] DATE
)
INSERT INTO #IndexDate (
Id, Date
)
VALUES
(1, CONCAT(YEAR(GETDATE()), '-01-01')),
(2, CONCAT(YEAR(GETDATE()), '-02-01')),
(3, CONCAT(YEAR(GETDATE()), '-03-01')),
(4, CONCAT(YEAR(GETDATE()), '-04-01')),
(5, CONCAT(YEAR(GETDATE()), '-05-01')),
(6, CONCAT(YEAR(GETDATE()), '-06-01')),
(7, CONCAT(YEAR(GETDATE()), '-07-01')),
(8, CONCAT(YEAR(GETDATE()), '-08-01')),
(9, CONCAT(YEAR(GETDATE()), '-09-01')),
(10, CONCAT(YEAR(GETDATE()), '-10-01')),
(11, CONCAT(YEAR(GETDATE()), '-11-01')),
(12, CONCAT(YEAR(GETDATE()), '-12-01'))
SELECT * FROM #IndexDate
You could use recursive cte:
WITH cte(n) AS (
SELECT 1
UNION ALL
SELECT n+ 1
FROM cte
WHERE n < 12
)
SELECT *
FROM cte
OPTION (maxrecursion 0);
WITH cte(d) AS (
SELECT CAST('20190101' AS DATE)
UNION ALL
SELECT DATEADD(m, 1, d)
FROM cte
WHERE d < '20200101'
)
SELECT *
FROM cte
OPTION (maxrecursion 0);
db<>fiddle demo
To match your logic with a recursive CTE, you can do:
with indextable as (
select 1 as id, datefromparts(year(getdate()), 1, 1) as date
union all
select 1 + id, dateadd(month, 1, date)
from indextable
where id < 12
)
select *
from indextable;
For one year, you don't have to worry about option (maxrecursion).
I'm not a big fan of using date as a column name, because it is a keyword, but SQL Server allows it.

Deleting Duplicate Row Combinations in a given Date

So I have a dataset of about 160 000 entries, they are computer generated and over the years mistakes happened.
Lets say the Table has the following columns:
- EntryID (auto int)
- FruitNumber
- JuiceNumber
- CandyNumber
- Date
Now the important thing is each combination of FruitNumber, JuiceNumber,CandyNumber is Unique when the time between them is less than 12 Months.
That means every exact combination of these can only exist once in 12 months. Now I need to get this dataset migrated into a new data model and for this I need to delete duplicate records (but keep 1 of them), I tried around alot with Queries but wasn´t able to find a solution.
Try to use cte:
;WITH cte AS
(
SELECT
ft.EntryID
, ft.FruitNumber
, ft.JuiceNumber
, ft.CandyNumber
, ft.Date
, ROW_NUMBER() OVER (PARTITION BY ft.FruitNumber, ft.JuiceNumber, ft.CandyNumber
ORDER BY ft.FruitNumber) RN
, DENSE_RANK() OVER (ORDER BY ft.FruitNumber, ft.JuiceNumber, ft.CandyNumber)
AS Partitionid
, COUNT(1) OVER (PARTITION BY ft.FruitNumber, ft.JuiceNumber, ft.CandyNumber
ORDER BY ft.FruitNumber) as PartitionCNT
FROM FooTable ft
)
SELECT
t1.*
, DATEDIFF(DAY, t.Date, t1.Date) DATEDiff
FROM
cte t
INNER JOIN cte t1
ON t1.FruitNumber = t.FruitNumber
AND t1.JuiceNumber = t.JuiceNumber
AND t1.CandyNumber = t.CandyNumber
AND DATEDIFF(DAY, t.Date, t1.Date)>= 365
WHERE t.PartitionCNT > 1
And the sample data:
CREATE TABLE FooTable
(
EntryID INT IDENTITY(1, 1) PRIMARY KEY,
FruitNumber INT,
JuiceNumber INT,
CandyNumber INT,
[Date] DATETIME
);*/
INSERT INTO FooTable
VALUES
(1, 2, 3 , '2019-03-01 00:00:00.000'),
(1, 2, 3 , '2020-03-01 00:00:00.000'),
(4, 5, 6 , '2019-03-01 00:00:00.000'),
(7, 8, 9 , '2019-03-01 00:00:00.000'),
(10, 11, 12 , '2018-03-20 00:00:00.000'),
(13, 14, 15 , '2018-03-20 00:00:00.000'),
(16, 17, 18 , '2017-03-09 00:00:00.000'),
(16, 17, 18 , '2017-02-09 00:00:00.000'),
(22, 23, 34 , '2017-02-12 00:00:00.000'),
(22, 23, 34 , '2017-02-12 00:00:00.000');
And OUTPUT:
EntryID FruitNumber JuiceNumber CandyNumber
2 1 2 3
If the errors are merely occasional, then this is likely to work:
select t.*
from (select t.*,
lag(date) over (partition by FruitNumber, JuiceNumber, CandyNumber) as prev_date
from t
) t
where prev_date is null or prev_date < dateadd(year, -1, date);
This is not a general solution -- although you can run this query multiple times. In particular, this only works if you have at most one duplicate during a year.
Unfortunately, the general solution requires recursive CTEs. For instance, if you have a record every month, it is tricky to figure out how to keep the "january" records.

DateDiff and Continuous periods

I need to see if one date span, in number of days, matches that of two date spans that cover the same period, but are added together.
For example...
DECLARE #Table1 TABLE
(
Id INT,
StartDate DATETIME,
EndDate DATETIME
)
INSERT INTO #Table1 VALUES (1, '2015-07-01 00:00:00.000', '2016-06-30 00:00:00.000')
DECLARE #Table2 TABLE
(
Id INT,
Fk INT,
StartDate DATETIME,
EndDate DATETIME
)
INSERT INTO #Table2 VALUES (1, 1, '2015-07-01', '2015-08-31')
INSERT INTO #Table2 VALUES (2, 1, '2015-09-01', '2016-03-31')
INSERT INTO #Table2 VALUES (3, 1, '2016-04-01', NULL)
SELECT DATEDIFF(DAY, T1.StartDate, T1.EndDate) AS SiteContractDays,
DATEDIFF(DAY, T2.StartDate, ISNULL(T2.EndDate, T1.EndDate)) AS SummedDayes
FROM #Table1 t1
INNER JOIN #Table2 t2
ON t2.fk = t1.Id
SELECT T1.Id, DATEDIFF(DAY, T1.StartDate, T1.EndDate) AS SiteContractDays,
SUM(DATEDIFF(DAY, T2.StartDate, ISNULL(T2.EndDate, T1.EndDate))) AS SummedDayes
FROM #Table1 t1
INNER JOIN #Table2 t2
ON t2.fk = t1.Id
GROUP BY T1.id, T1.StartDate, T1.EndDate
Dates are continuous.. they follow on for the full period. However, when I sum them up, we're short a few days. I'm not sure I can simply add a day to each DateDiff, because... then the total goes to 366, and the summed up values will go up as well.
I could add " + COUNT(*) -1 AS" to the SUM of the days when grouping them up, but that seems like a hack.
Maybe the + 1 is better but if you calculate by seconds it will be 1460 days in the end
SELECT (DATEDIFF(SECOND, '2015-07-01 00:00:00', '2016-06-30 23:59:59') + DATEDIFF(SECOND, '2016-07-01 00:00:00', '2019-06-30 23:59:59')) / 60 / 60 / 24
-- 1460
Two different statements that you need to understand.
SELECT DATEDIFF(DAY, '2015-07-01 00:00:00.000', '2019-06-30 00:00:00.000') -- 1460
No break thus count is continuous from day 1.
Day 1 from 2015-07-02
SELECT DATEDIFF(DAY, '2015-07-01', '2016-06-30') + DATEDIFF(DAY, '2016-07-01', '2019-06-30 00:00:00.000') -- 1459
Two different starting dates thus two different day 1s so you won't assume this is continuous...
day 1 from 2015-07-02 and day 1 from 2016-07-02... Day between 2016-06-30 and 2016-07-01 is your break and not counted.
Perhaps in breaking periods, you always need to add that 1 missing second explicitly.
SELECT
DATEDIFF(DAY, '2015-07-01 00:00:00.000', '2016-06-30') +
DATEDIFF(DAY, '2016-07-01', DATEADD(SECOND,1,'2019-06-30 23:59:59.000')) --1460

Summing up the records as per given conditions

I have a table like below, What I need that for any particular fund and up to any particular date logic will sum the amount value. Let say I need the sum for 3 dates as 01/28/2015,03/30/2015 and 04/01/2015. Then logic will check for up to first date how many records are there in table . If it found more than one record then it'll sum the amount value. Then for next date it'll sum up to the next date but from the previous date it had summed up.
Id Fund Date Amount
1 A 01/20/2015 250
2 A 02/28/2015 300
3 A 03/20/2015 400
4 A 03/30/2015 200
5 B 04/01/2015 500
6 B 04/01/2015 600
I want result to be like below
Id Fund Date SumOfAmount
1 A 02/28/2015 550
2 A 03/30/2015 600
3 B 04/01/2015 1100
Based on your question, it seems that you want to select a set of dates, and then for each fund and selected date, get the sum of the fund amounts from the selected date to the previous selected date. Here is the result set I think you should be expecting:
Fund Date SumOfAmount
A 2015-02-28 550.00
A 2015-03-30 600.00
B 2015-04-01 1100.00
Here is the code to produce this output:
DECLARE #Dates TABLE
(
SelectedDate DATE PRIMARY KEY
)
INSERT INTO #Dates
VALUES
('02/28/2015')
,('03/30/2015')
,('04/01/2015')
DECLARE #FundAmounts TABLE
(
Id INT PRIMARY KEY
,Fund VARCHAR(5)
,Date DATE
,Amount MONEY
);
INSERT INTO #FundAmounts
VALUES
(1, 'A', '01/20/2015', 250)
,(2, 'A', '02/28/2015', 300)
,(3, 'A', '03/20/2015', 400)
,(4, 'A', '03/30/2015', 200)
,(5, 'B', '04/01/2015', 500)
,(6, 'B', '04/01/2015', 600);
SELECT
F.Fund
,D.SelectedDate AS Date
,SUM(F.Amount) AS SumOfAmount
FROM
(
SELECT
SelectedDate
,LAG(SelectedDate,1,'1/1/1900') OVER (ORDER BY SelectedDate ASC) AS PreviousDate
FROM #Dates
) D
JOIN
#FundAmounts F
ON
F.Date BETWEEN DATEADD(DAY,1,D.PreviousDate) AND D.SelectedDate
GROUP BY
D.SelectedDate
,F.Fund
EDIT: Here is alternative to the LAG function for this example:
FROM
(
SELECT
SelectedDate
,ISNULL((SELECT TOP 1 SelectedDate FROM #Dates WHERE SelectedDate < Dates.SelectedDate ORDER BY SelectedDate DESC),'1/1/1900') AS PreviousDate
FROM #Dates Dates
) D
If i change your incorrect sample data to ...
CREATE TABLE TableName
([Id] int, [Fund] varchar(1), [Date] datetime, [Amount] int)
;
INSERT INTO TableName
([Id], [Fund], [Date], [Amount])
VALUES
(1, 'A', '2015-01-28 00:00:00', 250),
(2, 'A', '2015-01-28 00:00:00', 300),
(3, 'A', '2015-03-30 00:00:00', 400),
(4, 'A', '2015-03-30 00:00:00', 200),
(5, 'B', '2015-04-01 00:00:00', 500),
(6, 'B', '2015-04-01 00:00:00', 600)
;
this query using GROUP BY works:
SELECT MIN(Id) AS Id,
MIN(Fund) AS Fund,
[Date],
SUM(Amount) AS SumOfAmount
FROM dbo.TableName t
WHERE [Date] IN ('01/28/2015','03/30/2015','04/01/2015')
GROUP BY [Date]
Demo
Initially i have used Row_number and month function to pick max date of every month and in 2nd cte i did sum of amounts and joined them..may be this result set matches your out put
declare #t table (Id int,Fund Varchar(1),Dated date,amount int)
insert into #t (id,Fund,dated,amount) values (1,'A','01/20/2015',250),
(2,'A','01/28/2015',300),
(3,'A','03/20/2015',400),
(4,'A','03/30/2015',200),
(5,'B','04/01/2015',600),
(6,'B','04/01/2015',500)
;with cte as (
select ID,Fund,Amount,Dated,ROW_NUMBER() OVER
(PARTITION BY DATEDIFF(MONTH, '20000101', dated)ORDER BY dated desc)AS RN from #t
group by ID,Fund,DATED,Amount
),
CTE2 AS
(select SUM(amount)Amt from #t
GROUP BY MONTH(dated))
,CTE3 AS
(Select Amt,ROW_NUMBER()OVER (ORDER BY amt)R from cte2)
,CTE4 AS
(
Select DISTINCT C.ID As ID,
C.Fund As Fund,
C.Dated As Dated
,ROW_NUMBER()OVER (PARTITION BY RN ORDER BY (SELECT NULL))R
from cte C INNER JOIN CTE3 CC ON c.RN = CC.R
Where C.RN = 1
GROUP BY C.ID,C.Fund,C.RN,C.Dated )
select C.R,C.Fund,C.Dated,cc.Amt from CTE4 C INNER JOIN CTE3 CC
ON c.R = cc.R
declare #TableName table([Id] int, [Fund] varchar(1), [Date] datetime, [Amount] int)
declare #Sample table([SampleDate] datetime)
INSERT INTO #TableName
([Id], [Fund], [Date], [Amount])
VALUES
(1, 'A', '20150120 00:00:00', 250),
(2, 'A', '20150128 00:00:00', 300),
(3, 'A', '20150320 00:00:00', 400),
(4, 'A', '20150330 00:00:00', 200),
(5, 'B', '20150401 00:00:00', 500),
(6, 'B', '20150401 00:00:00', 600)
INSERT INTO #Sample ([SampleDate])
values ('20150128 00:00:00'), ('20150330 00:00:00'), ('20150401 00:00:00')
-- select * from #TableName
-- select * from #Sample
;WITH groups AS (
SELECT [Fund], [Date], [AMOUNT], MIN([SampleDate]) [SampleDate] FROM #TableName
JOIN #Sample ON [Date] <= [SampleDate]
GROUP BY [Fund], [Date], [AMOUNT])
SELECT [Fund], [SampleDate], SUM([AMOUNT]) FROM groups
GROUP BY [Fund], [SampleDate]
Explanation:
The CTE groups finds the earliest SampleDate which is later than (or equals to) your
data's date and enriches your data accordingly, thus giving them the group to be summed up in.
After that, you can group on the derived date.