Date comparison on SQL - sql

I want to get specific total amount on query but it needs to be compared on two dates, Due Date and Payment Date. I already do some query but I always found problem when comparing Payment Date and Due Dates.
SELECT ContractID, DueDate, PaymentDate, OutstandingBalance [Outstanding], MonthlyAmortization,
row_number() over(partition by ContractID order by DueDate desc) as rn
FROM [div].[ClientsLedger]
WHERE DueDate BETWEEN '1/1/2000' AND '1/31/2023' AND PaymentDate IS NOT NULL AND ContractID = 156
Here is the result of first query with late payment, I want to get rid of the first row because it is late as of December and retain the rest of the result.
SELECT ContractID, DueDate, PaymentDate, OutstandingBalance [Outstanding], MonthlyAmortization,
row_number() over(partition by ContractID order by DueDate desc) as rn
FROM [div].[ClientsLedger]
WHERE DueDate BETWEEN '1/1/2000' AND '1/31/2023' AND PaymentDate IS NOT NULL AND ContractID = 2254
This is the expected result when there is no late payment.
I already solved the issue thanks for the help, I created a query where the first result is separated to perform the comparison and combine with the rest of the result.
SELECT TOP 1 ContractID, DueDate,
CASE
WHEN DueDate >= PaymentDate THEN PaymentDate
ELSE NULL
END AS PaymentDate,
OutstandingBalance [Outstanding], MonthlyAmortization,
row_number() over(partition by ContractID order by DueDate desc) as rn
FROM [div].[ClientsLedger]
WHERE
DueDate BETWEEN '1/1/2000' AND '1/31/2023'
AND PaymentDate IS NOT NULL
AND ContractID = 156
UNION ALL
SELECT ContractID, DueDate, PaymentDate, OutstandingBalance [Outstanding], MonthlyAmortization, row_number() over(partition by ContractID order by DueDate desc) as rn FROM [div].[ClientsLedger]
WHERE
DueDate BETWEEN '1/1/2000' AND DATEADD(MONTH, -2, '1/31/2023')
AND PaymentDate IS NOT NULL
AND ContractID = 156

'1/1/2000' and '1/31/2023' are strings, not dates. If DueDate is also a string, BETWEEN '1/1/2000' AND '1/31/2023' will result in a string comparison using string ordering rules, which won't match anything. If DueDate is a date or datetime, the result will be a cast error in 95% of the world as SQL Server tries to cast that string into a date before comparing it to the actual date values.
The real solution is to use a suitable date type for DueDate, eg date or datetime2 and use date-typed parameters to pass actual dates to the query. If you absolutely have to, use the unambiguous, unseparated date format for the comparison values only, ie DueDate BETWEEN '20000101' and '20230131'. Any other string will get converted based on the server's locale settings:
declare #from date='20000101', #to date='20230131'
SELECT ...
FROM [div].[ClientsLedger]
WHERE DueDate BETWEEN #from AND #to ...
If the field type is date, datetime2 or datetimeoffset you can also use the ISO8601 format, YYYY-MM-DD. The legacy datetime type will treat this as a localized string though. If you have to hard-code a date value, it's safer to use YYYYMMDD
Calculate the dates on the fly
Another option is to calculate the end date on the fly. It seems that the start date is a fixed date in the past, while the end date is the end of the past month. You can use calculate this on the fly with EOMONTH
declare #to date=EOMONTH(GetDate(),-1)

Related

sql query group by date range

I have table with below data -
StartDate EndDate Amount
2/1/2016 4/30/2016 2.265
2/1/2016 12/31/2099 16.195
5/1/2016 12/31/2099 37.75
I am trying to write a query to sum the amount on date range and give me below result
StartDate EndDate Amount
2/1/2016 4/30/2016 18.46
5/1/2016 12/31/2099 53.945
The result needs to be distinct date range with amount summed for that date range. As in above example, row 2 has dates that overlap row 1 and 3. So the row 2 amount needs to be added in row 1 and row 3.
I am writing this query on sql server 2012, Please advise on what approach I should take.
Below is query to generate sample data
SELECT * INTO #tmp_GridResults_1
FROM (
SELECT N'2016-02-01 00:00:00.000' AS [StartDate], N'2016-04-30 00:00:00.000' AS [EndDate], N'2.265' AS [Amount] UNION ALL
SELECT N'2016-02-01 00:00:00.000' AS [StartDate], N'2099-12-31 00:00:00.000' AS [EndDate], N'16.195' AS [Amount] UNION ALL
SELECT N'2016-05-01 00:00:00.000' AS [StartDate], N'2099-12-31 00:00:00.000' AS [EndDate], N'37.75' AS [Amount] ) t;
SELECT [StartDate], [EndDate], [Amount]
FROM #tmp_GridResults_1
I don't think you'll be able to group with both StartDate and EndDate. However, if you use just StartDate and Amount you can try this:
select StartDate,sum(Amount) as Amount from #tmp_GridResults_1 group by StartDate
This will give you the grouping of Amount by StartDate.
From my previews comment i think you want to group by StartDate, in that case you can use:
SELECT
mt.StartDate,
SUM(Amount) AS 'Amount'
FROM MyTable mt
WHERE mt.StartDate BETWEEN
'2016-05-18 00:00:00.000' AND '2016-06-20 00:00:00.000' --your date range
GROUP BY StartDate
However you need to specify in your question how you want to group them, because with 2 date fields you can group the following ways:
GROUP BY StartDate --groups records that have the same StartDate
GROUP BY EndDate --groups records that have the same EndDate
GROUP BY StartDate, EndDate --groups records that have same StartDate and EndDate
Also with 2 date fields your date range can vary a lot.
WHERE StartDate BETWEEN #sd AND #ed --will get records whose start date is inside the provided range
WHERE EndDate BETWEEN #sd AND #ed --will get records whose end date is inside the provided range
WHERE StartDate >= #sd AND EndDate <= #ed -- will get records that started and ended inside the provided date range
WHERE StartDate BETWEEN #sd AND #ed OR EndDate BETWEEN #sd AND #ed
--this last one will get records whose StartDate or EndDate are inside the provided date range
based on this you can build the query that you need, but with the provided data in your question its still ambiguous what you want to achieve, you need to be more specific and provide more data in order to produce an exact answer to your question.
One more thing to add is that if you take into account time it will not group them if the date is the same but time is different i.e.
'2016-05-18 00:00:00.000'
'2016-05-18 01:01:01.001'
--these dates will not be grouped
Below query will give the required result :-
select StartDate,EndDate,SUM(Amount) over (partition by StartDate) AS Amount into #t1
from table_name
select StartDate,EndDate,SUM(Amount) over (partition by EndDate) AS Amount into #t2
from table_name
select * from (
select distinct P.StartDate,P.EndDate,P.Amount from (
select StartDate,MIN(EndDate) EndDate,Amount from #t1
group by StartDate,Amount) P
JOIN (
select MIN(StartDate) StartDate,EndDate,Amount from #t2
group by EndDate,Amount) Q on P.StartDate=Q.StartDate OR P.EndDate=Q.EndDate
where P.Amount>=Q.Amount
UNION
select distinct P.StartDate,P.EndDate,P.Amount from (
select MIN(StartDate) StartDate,EndDate,Amount from #t2
group by EndDate,Amount) P
JOIN (
select StartDate,MIN(EndDate) EndDate,Amount from #t1
group by StartDate,Amount) Q on P.StartDate=Q.StartDate OR P.EndDate=Q.EndDate
where P.Amount>=Q.Amount
) A
Just Replace table_name by the table for which you want result, Let me know in case of any confusion

SQL Sum of orders by date

I should know this but for some reason its getting me stumped.
This simple code is outputting all orders by day
USE [K.1]
Select CreatedAt,Identifier,RoundedPriceSum from StarOrder
where SiteID = 1
and OrderType <>2
and CreatedAt between '2015/01/01' and '2015/08/20'
CreatedAt is a date, Identifier is unique order ID and RoundedPriceSum the total of the order.
Is it possible to amend the code to provide a total of RoundedPriceSum per day_
Use GROUP BY:
Select cast(CreatedAt as date) as CreatedDay, SUM(RoundedPriceSum)
from StarOrder so
where SiteID = 1 and OrderType <> 2 and
CreatedAt >= '2015-01-01' and
CreatedAt < '2015/08/20'
group by cast(CreatedAt as date)
order by CreatedDay;
Notes on changes to the query:
Changed the dates to ISO standard YYYY-MM-DD format.
Replaced the BETWEEN with >= and <. This works better for dates with times.
Use cast(as date) to remove the time component.
Added an ORDER BY so the results are in order by day.
select s.CreatedAt,s.Identifier,x.tot
from StarOrder s
join
(select CreatedAt,sum(RoundedPriceSum) as tot
from StarOrder
where SiteID = 1
and OrderType <>2
and CreatedAt between '2015/01/01' and '2015/08/20'
group by createdat) x
on x.createdat = s.createdat
where SiteID = 1
and OrderType <>2
and s.CreatedAt between '2015/01/01' and '2015/08/20'

SSRS Convert date and groupby

I have a DateTime type column called CreatedDate in my SQL table, and I have created a report using SSRS 2008.
I have grouped my table in my report with this CreatedDate.
I need to orderby this CreatedDate in the format of dd/MM/yyyy.
If I convert this column value as Convert(varchar(10),CreateDate,101) am getting the values in the format of MM/dd/yyyy and its sorting the datas correctly like,
03/03/2012
03/05/2012
05/03/2012
The same way If I convert the column as Convert(varchar(10),CreatedDate,103) am getting the values in the format of dd/MM/yyyy and its sorting like
03/03/2012
04/05/2012
05/03/2012
but I need to group the table like
03/03/2012
05/03/2012
04/05/2012
like this, for this I tried giving the sort function in the tablix manually like
=Format(Fields!CreatedDate.value,"dd/MM/yyyy")
but its not working, how can I fix this....can anyone help me here...
the query am using here is
SELECT ItemName
, COUNT(ItemName) AS Quantity
, SUM(LineTotal) AS Amount
, CONVERT(varchar(10), CreatedDate, 103) AS CreatedDate
FROM StudentFeesItems
WHERE (CONVERT(varchar(10), CreatedDate, 101) BETWEEN #StartDate AND #EndDate)
GROUP BY ItemName, CreatedDate
You can add the date on those 2 formats on the dataset and then sort by one of them:
SELECT [YourColumns], Convert(varchar(8),CreateDate,112) SortDate,
Convert(varchar(10),CreatedDate,103) Displaydate
FROM YourTable
This way, you can use DisplayDate on your tablix and sort manually by SortDate.
Don't convert your date to strings. Keep them as dates and sort them as dates.
As you've already experienced, unless you have the string formatted as YYYY-MM-DD you won't get the order that you want. If you keep the date as a DATETIME datatype, you can order by it, group by it, format it and do what you like to it.
DATEADD(DAY, DATEDIFF(DAY, 0, CreateDate), 0) AS roundedDate
RE-EDIT
Here is my suggestion in your query...
WITH
main_query
AS
(
SELECT
ItemName,
DATEADD(DAY, DATEDIFF(DAY, 0, CreatedDate), 0) AS CreatedDate,
COUNT(ItemName) AS Quantity,
SUM(LineTotal) AS Amount
FROM
StudentFeesItems
WHERE
CreatedDate >= #StartDate
AND CreatedDate < DATEADD(DAY, 1, #EndDate)
GROUP BY
ItemName,
DATEADD(DAY, DATEDIFF(DAY, 0, CreatedDate), 0)
)
SELECT
ItemName,
Convert(varchar(10), CreatedDate, 103) AS formattedDate,
Quantity,
Amount
FROM
main_query
ORDER BY
ItemName,
CreatedDate
Notes:
- Don't call a function on a field and THEN filter it
- Don't use string manilpulation to round a datetime to a date
- Don't sort by the string representation of dates
how about adding a ROW_NUMBER() over (order by CreatedDate) as myOrder on your sql query and order by myOrder?
EDIT:
=Format(Fields!date.Value,"dd/MM/yyyy")

How to count number of records per day?

I have a table in a with the following structure:
CustID --- DateAdded ---
396 2012-02-09
396 2012-02-09
396 2012-02-08
396 2012-02-07
396 2012-02-07
396 2012-02-07
396 2012-02-06
396 2012-02-06
I would like to know how I can count the number of records per day, for the last 7 days in SQL and then return this as an integer.
At present I have the following SQL query written:
SELECT *
FROM Responses
WHERE DateAdded >= dateadd(day, datediff(day, 0, GetDate()) - 7, 0)
RETURN
However this only returns all entries for the past 7 days. How can I count the records per day for the last 7 days?
select DateAdded, count(CustID)
from Responses
WHERE DateAdded >=dateadd(day,datediff(day,0,GetDate())- 7,0)
GROUP BY DateAdded
select DateAdded, count(CustID)
from tbl
group by DateAdded
about 7-days interval it's DB-depending question
SELECT DateAdded, COUNT(1) AS NUMBERADDBYDAY
FROM Responses
WHERE DateAdded >= dateadd(day,datediff(day,0,GetDate())- 7,0)
GROUP BY DateAdded
This one is like the answer above which uses the MySql DATE_FORMAT() function. I also selected just one specific week in Jan.
SELECT
DatePart(day, DateAdded) AS date,
COUNT(entryhash) AS count
FROM Responses
where DateAdded > '2020-01-25' and DateAdded < '2020-02-01'
GROUP BY
DatePart(day, DateAdded )
If your timestamp includes time, not only date, use:
SELECT DATE_FORMAT('timestamp', '%Y-%m-%d') AS date, COUNT(id) AS count FROM table GROUP BY DATE_FORMAT('timestamp', '%Y-%m-%d')
You could also try this:
SELECT DISTINCT (DATE(dateadded)) AS unique_date, COUNT(*) AS amount
FROM table
GROUP BY unique_date
ORDER BY unique_date ASC
SELECT count(*), dateadded FROM Responses
WHERE DateAdded >=dateadd(day,datediff(day,0,GetDate())- 7,0)
group by dateadded
RETURN
This will give you a count of records for each dateadded value. Don't make the mistake of adding more columns to the select, expecting to get just one count per day. The group by clause will give you a row for every unique instance of the columns listed.
select DateAdded, count(DateAdded) as num_records
from your_table
WHERE DateAdded >=dateadd(day,datediff(day,0,GetDate())- 7,0)
group by DateAdded
order by DateAdded
Unfortunately the best answer here IMO is a comment by #Profex on an incorrect answer , but the solution I went with is
SELECT FORMAT(DateAdded, 'yyyy-MM-dd'), count(CustID)
FROM Responses
WHERE DateAdded >= dateadd(day,datediff(day,0,GetDate())- 7,0)
GROUP BY FORMAT(DateAdded, 'yyyy-MM-dd')
ORDER BY FORMAT(DateAdded, 'yyyy-MM-dd')
Note that I haven't tested this SQL since I don't have the OP's DB , but this approach works well in my scenario where the date is stored to the second
The important part here is using the FORMAT(DateAdded, 'yyyy-MM-dd') method to drop the time without losing the year and month , as would happen if you used DATEPART(day, DateAdded)
When a day among last 7 days, has no record means, the following code will list out that day with count as zero.
DECLARE #startDate DATE = GETDATE() - 6,
#endDate DATE = GETDATE();
DECLARE #daysTable TABLE
(
OrderDate date
)
DECLARE #daysOrderTable TABLE
(
OrderDate date,
OrderCount int
)
Insert into #daysTable
SELECT TOP (DATEDIFF(DAY, #startDate, #endDate) + 1)
Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, #startDate)
FROM sys.all_objects a
CROSS JOIN sys.all_objects b;
Insert into #daysOrderTable
select OrderDate, ISNULL((SELECT COUNT(*) AS OdrCount
FROM [dbo].[MyOrderTable] odr
WHERE CAST(odr.[CreatedDate] as date) = dt.OrderDate
group by CAST(odr.[CreatedDate] as date)
), 0) AS OrderCount from #daysTable dt
select * from #daysOrderTable
RESULT
OrderDate     OrderCount
2022-11-22     42
2022-11-23     6
2022-11-24     34
2022-11-25     0
2022-11-26     28
2022-11-27     0
2022-11-28     22
SELECT DATE_FORMAT(DateAdded, '%Y-%m-%d'),
COUNT(CustID)
FROM Responses
GROUP BY DATE_FORMAT(DateAdded, '%Y-%m-%d');

How do I get a maximium daily value of a numerical field over a year in SQL

How do I get a maximium daily value of a numerical field over a year in MS-SQL
This would query the daily maximum of value over 2008:
select
datepart(dayofyear,datecolumn)
, max(value)
from yourtable
where '2008-01-01' <= datecolumn and datecolumn < '2009-01-01'
group by datepart(dayofyear,datecolumn)
Or the daily maximum over each year:
select
datepart(year,datecolumn),
, datepart(dayofyear,datecolumn)
, max(value)
from yourtable
group by datepart(year,datecolumn), datepart(dayofyear,datecolumn)
Or the day(s) with the highest value in a year:
select
Year = datepart(year,datecolumn),
, DayOfYear = datepart(dayofyear,datecolumn)
, MaxValue = max(MaxValue)
from yourtable
inner join (
select
Year = datepart(year,datecolumn),
, MaxValue = max(value)
from yourtable
group by datepart(year,datecolumn)
) sub on
sub.Year = yourtable.datepart(year,datecolumn)
and sub.MaxValue = yourtable.value
group by
datepart(year,datecolumn),
datepart(dayofyear,datecolumn)
You didn't mention which RDBMS or SQL dialect you're using. The following will work with T-SQL (MS SQL Server). It may require some modifications for other dialects since date functions tend to change a lot between them.
SELECT
DATEPART(dy, my_date),
MAX(my_number)
FROM
My_Table
WHERE
my_date >= '2008-01-01' AND
my_date < '2009-01-01'
GROUP BY
DATEPART(dy, my_date)
The DAY function could be any function or combination of functions which gives you the days in the format that you're looking to get.
Also, if there are days with no rows at all then they will not be returned. If you need those days as well with a NULL or the highest value from the previous day then the query would need to be altered a bit.
Something like
SELECT dateadd(dd,0, datediff(dd,0,datetime)) as day, MAX(value)
FROM table GROUP BY dateadd(dd,0, datediff(dd,0,datetime)) WHERE
datetime < '2009-01-01' AND datetime > '2007-12-31'
Assuming datetime is your date column, dateadd(dd,0, datediff(dd,0,datetime)) will extract only the date part, and then you can group by that value to get a maximum daily value. There might be a prettier way to get only the date part though.
You can also use the between construct to avoid the less than and greater than.
Group on the date, use the max delegate to get the highest value for each date, sort on the value, and get the first record.
Example:
select top 1 theDate, max(theValue)
from TheTable
group by theDate
order by max(theValue) desc
(The date field needs to only contain a date for this grouping to work, i.e. the time component has to be zero.)
If you need to limit the query for a specific year, use a starting and ending date in a where claues:
select top 1 theDate, max(theValue)
from TheTable
where theDate between '2008-01-01' and '2008-12-13'
group by theDate
order by max(theValue) desc