How to get the sum of values in a table interval? - sql

The question is very simple :-). I'm a beginner.
tables
Declare #startDate date,
Declare #endDate date
Select
d.ID,
d.Date as DateDocumet,
dt.id As TypeDocument,
p.Name as ProductName,
p.Price as Price,
d.qty
from Documents d
LEFT join product p on d.ProductId = p.id
LEFT join DocumentType dt on d.DocumentTypeId = dt.id
Result:
A taskā€¦..
There are two date variables.
How to get the sum of values(qty) between dates(#startDate - #endDate).
How to get the sum of values(qty) up to #startDate.
How get the sum of values(qty) down to #endDate.
If DocumentType is 1. Then the value(qty) minus.

Looks like you need conditional aggregation SUM(CASE WHEN....
CROSS APPLY also makes it easier to pre-calculate the negative qty
Declare #startDate date;
Declare #endDate date;
Select
SUM(CASE WHEN d.Date < #startDate THEN QtyToSum END),
SUM(CASE WHEN d.Date >= #startDate AND d.Date < #endDate THEN QtyToSum END),
SUM(CASE WHEN d.Date >= #endDate THEN QtyToSum END),
from Documents d
CROSS APPLY (VALUES (CASE WHEN d.DocumentTypeId = 1 THEN -d.qty ELSE d.qty END) ) AS v(QtyToSum)

Related

How do I include records in a Summary query to include those that don't have data?

I have a query on a transaction table that returns the Summarized total on a column for each ID based on a data range. The query works great except it doesn't include those IDs that don't have data in the transaction table. How can I include those IDs in my result filled with a zero total. Here's a simplified version of my query.
SELECT tblID.IDName
,SUM(CASE
WHEN tblTransactions.idxTransType = 30
THEN CAST(tblTransactions.TimeAmount AS FLOAT) / 60.0
ELSE 0
END) AS 'Vacation'
FROM tblTransactions
INNER JOIN tblTransTypes ON tblTransactions.idxTransType = tblTransTypes.IdxTransType
INNER JOIN tblID ON tblTransactions.idxID = tblID.IdxID
WHERE (tblTransactions.Deleted = 0)
AND (tblTransactions.NotCurrent = 0)
AND (tblTransactions.TransDate >= CONVERT(DATETIME, 'March 1, 2018', 102))
AND (tblTransactions.TransDate <= CONVERT(DATETIME, 'April 11, 2018', 102))
GROUP BY tblID.IDName
Actually it's slightly more complicated than that:
SELECT
i.IDName,
SUM(CASE WHEN t.idxTransType = 30 THEN CAST(t.TimeAmount AS FLOAT) / 60.0 ELSE 0 END) AS 'Vacation'
FROM
tblID i
LEFT JOIN tblTransactions t ON t.idxID = i.IdxID AND t.Deleted = 0 AND t.NotCurrent = 0 AND t.TransDate BETWEEN '20180301' AND '20180411'
LEFT JOIN tblTransTypes tt ON tt.IdxTransType = t.idxTransType
GROUP BY
i.IDName;
You want left joins:
SELECT i.IDName,
SUM(CASE WHEN t.idxTransType = 30 THEN CAST(t.TimeAmount AS Float) / 60.0 ELSE 0 END) AS Vacation
FROM tblID i LEFT JOIN
tblTransactions t
ON t.idxID = i.IdxID AND
t.Deleted = 0 AND
t.NotCurrent = 0 AND
t.TransDate >= '2018-03-01' AND
t.TransDate <= '2018-04-11'
tblTransTypes tt
ON t.idxTransType = tt.IdxTransType
GROUP BY i.IDName;
Notes:
Table aliases make the query much easier to write and to read.
Use ISO/ANSI standard date formats.
The filter conditions on all but the first table belong in the ON clauses.

FInd rows with 0 count from group by sql

I have a select query to select an ID and corresponding count and group them based on ID and this is the sql for that
SELECT InID,
COUNT(*) as ICount
FROM RawData
WHERE CompletedDate>= #BeginDate AND CompletedDate<= #EndDate
AND InID in (3851,4151,11)
GROUP BY InID
This returns only one record for me
4151 225
To find the missing entries ie 3851 and 11 , i tried the query
SELECT InID,
COUNT(*) as ICount
FROM RawData
WHERE CompletedDate>= #BeginDate AND CompletedDate<= #EndDate
AND InID in (3851,4151,11)
GROUP BY InID
HAVING COUNT(*)=0
But it returned 0 records. So to check the IDs with missing records in a group by what is the proper way
This won't return you a row if there are no records for the specified Id's That satisfy the condition in the Where Clause. You Can try this Logic
;WITH CTE
AS
(
SELECT MyId = 3851
UNION ALL
SELECT 4151
UNION ALL
SELECT 11
)
SELECT
CTE.MyId,
COUNT(1)
FROM CTE
LEFT JOIN RawData RD
ON CTE.MyId = RD.InID
WHERE CompletedDate>= #BeginDate
AND CompletedDate<= #EndDate
GROUP BY CTE.MyId
Or Simply this will also work for you
select
InID,
SUM(CASE WHEN CompletedDate>= #BeginDate
AND CompletedDate<= #EndDate THEN 1 ELSE 0 END) as ICount
FROM RawData
whereInID in (3851,4151,11)
group by InID
To Filter records with Zero count
select
InID,
SUM(CASE WHEN CompletedDate>= #BeginDate
AND CompletedDate<= #EndDate THEN 1 ELSE 0 END) as ICount
FROM RawData
whereInID in (3851,4151,11)
group by InID
having SUM(CASE WHEN CompletedDate>= #BeginDate
AND CompletedDate<= #EndDate THEN 1 ELSE 0 END) = 0
TRY THIS: I think you can achieve your desired output through OUTER APPLY as below:
SELECT InID, rd1.ICount
FROM RawData rd
OUTER APPLY (SELECT COUNT(*) ICount
FROM RowDate rd1
WHERE rd1.InID = rd.InID
AND rd1.CompletedDate>= #BeginDate
AND rd1.CompletedDate<= #EndDate) rd1
WHERE InID IN (3851,4151,11)
GROUP BY InID
IDs those doesn't have records will display NULL.
Using LEFT JOIN:
SELECT r.InID, ISNULL(c.ICount, 0) as ICount
FROM RawData r
LEFT JOIN (
select InID, COUNT(*) as ICount
FROM RawData
where CompletedDate>= #BeginDate AND CompletedDate<= #EndDate
and InID in (3851,4151,11)
group by InID
) c ON c.InID = r.InID
WHERE r.InID in (3851,4151,11)
Using OUTER APPLY:
SELECT r.InID, ISNULL(c.ICount, 0) as ICount
FROM RawData r
OUTER APPLY (
select COUNT(*) as ICount
FROM RawData
where CompletedDate>= #BeginDate AND CompletedDate<= #EndDate
and InID = r.InID
) c
WHERE r.InID in (3851,4151,11)
You need to do a left join from a resultset that contains all the values you want to include
select ids.Id, count(InId)
from
(select 3851 as Id union select 4151 union select 11) ids
left join
RawData
on ids.id = rawdata.inid
where CompletedDate>= #BeginDate AND CompletedDate<= #EndDate
group by Ids.id

Total Count of each

I have a problem with my SQL Server query.
My query must display the total number of each category. Not the total amount of all categories.
SELECT [CATEGORY],
(SELECT COUNT(*)
FROM [Group_New_DB].[dbo].[INCIDENTSM1]
WHERE ([OPEN_TIME] >= #StartDate and [OPEN_TIME] < #EndDate + 1) ) AS OpenedCount,
(SELECT COUNT(*)
FROM [Group_New_DB].[dbo].[INCIDENTSM1]
WHERE ([CLOSE_TIME] >= #StartDate and [CLOSE_TIME] < #EndDate + 1)) AS ClosedCount
FROM [Group_New_DB].[dbo].[INCIDENTSM1]
GROUP BY CATEGORY
ORDER BY CATEGORY
The report consists of a table with 3 columns: Category, Registered, Closed.
1 column is the category name.
2 column is how many categories have been registered.
3 column -> how many columns were closed.
But result
The result that turned out as a result does not look right.
I don't quite understand what is the difference between total number vs total amount as the question doesn't provide any context.
Although, if you are trying to get OpenedCount and ClosedCount as you have named the columns, I suggest you try below:
SELECT
CATEGORY,
SUM(CASE WHEN (OPEN_TIME >= #start_date AND OPEN_TIME < #end_date+1)
THEN 1
ELSE 0
END) AS OPENED_COUNT,
SUM(CASE WHEN (CLOSED_TIME >= #start_date AND CLOSED_TIME < #end_date+1)
THEN 1
ELSE 0
END) AS CLOSED_COUNT
FROM
[Group_New_DB].[dbo].[INCIDENTSM1]
GROUP BY
CATEGORY
ORDER BY
CATEGORY
IMO, This is also a better way as it doesn't include multiple sub-queries.
Correction: changed COUNT to SUM as suggested by TriV - Thanks!
Try this instead -
SELECT [CATEGORY],
COUNT(CASE WHEN [OPEN_TIME] >= #StartDate and [OPEN_TIME] < #EndDate + 1 THEN 1 ELSE NULL END) AS OpenedCount,
COUNT(CASE WHEN [CLOSE_TIME] >= #StartDate and [CLOSE_TIME] < #EndDate + 1 THEN 1 ELSE NULL END) AS ClosedCount
FROM [Group_New_DB].[dbo].[INCIDENTSM1] GROUP BY
CATEGORY ORDER BY CATEGORY

Calculate total business working days between two dates

select count(distinct(dateadd(d, 0, datediff(d, 0,checktime)))) as workingdays
from departments,
dbo.USERINFO INNER JOIN dbo.CHECKINOUT ON
dbo.USERINFO.USERID = dbo.CHECKINOUT.USERID
where userinfo.name='Gokul Gopalakrishnan' and deptname='GEN/SUP-TBL'
and checktime>='2014-05-01' and checktime<='2014-05-30'
from the above code I am able to find total working days of employee between two dates.
workingdays
20
but now I want other column name total business days. I want to calculate total business days between two dates.
workingdays businessdays
20 21
how can i do this?
If you only want to exclude weekends then you can simply just exclude these using a conditional count by adding:
count(distinct case when datepart(weekday, getdate()) <= 5 then date end)
So your query becomes:
set datefirst 1;
select count(distinct(dateadd(d, 0, datediff(d, 0,checktime)))) as workingdays,
count(distinct case when datepart(weekday, getdate()) <= 5
then dateadd(d, 0, datediff(d, 0,checktime))
end) as weekdays
from departments,
dbo.USERINFO INNER JOIN dbo.CHECKINOUT ON
dbo.USERINFO.USERID = dbo.CHECKINOUT.USERID
where userinfo.name='Gokul Gopalakrishnan' and deptname='GEN/SUP-TBL'
and checktime>='2014-05-01' and checktime<='2014-05-30'
HOWEVER I would really recommend adding a calendar table to your database. It makes everything so easy, your query would become:
SELECT DaysWorked = COUNT(cio.Date),
WeekDaysWorked = COUNT(CASE WHEN c.IsWeekDay = 1 THEN cio.Date END),
WorkingDaysWorked = COUNT(CASE WHEN c.IsWorkingDay = 1 THEN cio.Date END),
TotalDays = COUNT(*),
TotalWeekDays = COUNT(CASE WHEN c.IsWeekDay = 1 THEN 1 END),
TotalWorkingDays = COUNT(CASE WHEN c.IsWorkingDay = 1 THEN 1 END)
FROM dbo.Calender AS c
LEFT JOIN
( SELECT DISTINCT
Date = CAST(CheckTime AS DATE)
FROM dbo.Departments AS d
CROSS JOIN dbo.userInfo AS ui
INNER JOIN dbo.CheckInOut AS cio
ON cio.UserID = ui.UserID
WHERE ui.Name = 'Gokul Gopalakrishnan'
AND d.deptname = 'GEN/SUP-TBL'
) AS cio
ON c.Date = cio.Date
WHERE d.Date >= '2014-05-01'
AND d.Date <= '2014-05-30';
This way you can define public holidays, weekends, etc. It is so much more flexible than any other solution.
EDIT
I think I misunderstood your original criteria. This should work for you with no calendar table:
SET DATEFIRST 1;
DECLARE #StartDate DATE = '2014-05-01',
#EndDate DATE = '2014-05-30';
DECLARE #Workdays INT =
(DATEDIFF(DAY, #StartDate, #EndDate) + 1)
-(DATEDIFF(WEEK, #StartDate, #EndDate) * 2)
-(CASE WHEN DATEPART(WEEKDAY, #StartDate) = 7 THEN 1 ELSE 0 END)
-(CASE WHEN DATEPART(WEEKDAY, #EndDate) = 6 THEN 1 ELSE 0 END);
SELECT WorkingDays = COUNT(DISTINCT CAST(CheckTime AS DATE)),
BusinessDays = #Workdays
FROM dbo.Departments AS d
CROSS JOIN dbo.userInfo AS ui
INNER JOIN dbo.CheckInOut AS cio
ON cio.UserID = ui.UserID
WHERE ui.Name = 'Gokul Gopalakrishnan'
AND d.deptname = 'GEN/SUP-TBL'
AND cio.CheckTime >= #StartDate
AND cio.CheckTime <= #EndDate;
following query calculate Fridays count between #FromDate and #ToDate variable
((DATEDIFF(DAY,#FromDate,#ToDate)-(6-DATEPART(dw,#FromDate)))/7)*2
Following query calculate Working day count and business day count between to date :
DECLARE #FromDate DATE = '2014-05-01',
#ToDate DATE = '2014-05-30'
SELECT COUNT(DISTINCT CAST(checktime AS Date)) as workingdays,
DATEDIFF(DAY,#FromDate,#ToDate) -
((DATEDIFF(DAY,#FromDate,#ToDate)-(6-DATEPART(dw,#FromDate)))/7)*2 AS BusinessDay
from departments,
dbo.USERINFO INNER JOIN dbo.CHECKINOUT ON
dbo.USERINFO.USERID = dbo.CHECKINOUT.USERID
where userinfo.name='Gokul Gopalakrishnan' and deptname='GEN/SUP-TBL'
and checktime>= #FromDate and checktime<=#ToDate

T-SQL help needed to create a result set in SQL Server 2008

I have to display data in the following format:
Suppose I select 08-2013 date to display data. Actually this is a consumer data.So This month column should calculate data ( i.e. Count OR SUM ) for Aug-2013 and From April column should calculate data ( i.e. Count OR SUM ) from Apr-2013 to Aug-2013.
So now my problem is that how can i write T-SQL to get this result set.
I can calculate This month data and From April data separately but how to merge these two result in single result set.
Do I have to create a table to get this result set.
PS
OK so I take an example of first row 1) Sample text 1 just to represent individual calculation for This month and From April
DECLARE #BILL_DATE DATETIME
SET #BILL_DATE= '2013-08-15'
--Seprate result set for THIS MONTH and ROW For Sample text 1
SELECT
ISNULL(COUNT(CASE WHEN CAT_CODE='1' THEN C.CONSUMER_NO END),0) AS CAT_1_THIS_MONTH,
ISNULL(COUNT(CASE WHEN CAT_CODE='2' THEN C.CONSUMER_NO END),0) AS CAT_2_THIS_MONTH,
ISNULL(COUNT(CASE WHEN CAT_CODE='3' THEN C.CONSUMER_NO END),0) AS CAT_3_THIS_MONTH
FROM CONSUMER C
INNER JOIN BILLING B ON C.CONSUMER_NO = B.CONSUMER_NO
WHERE B.BILL_DATE = #BILL_DATE AND (more condition will be as per For Sample text 1)
--Seprate result set for FROM APRIL and ROW For Sample text 1
SELECT
ISNULL(SUM(CASE WHEN CAT_CODE='1' THEN C.UNIT END),0) AS CAT_1_FROM_APRIL,
ISNULL(SUM(CASE WHEN CAT_CODE='2' THEN C.UNIT END),0) AS CAT_2_FROM_APRIL,
ISNULL(SUM(CASE WHEN CAT_CODE='3' THEN C.UNIT END),0) AS CAT_3_FROM_APRIL
FROM CONSUMER C
INNER JOIN BILLING B ON C.CONSUMER_NO = B.CONSUMER_NO
WHERE B.BILL_DATE BETWEEN '2013-04-01' AND #BILL_DATE AND (more condition will be as per For Sample text 1)
Now my aim is to display these two individual result in one row in two column ( i.e. This month and From April )
You need a condition that will include the entire period in the WHERE clause
Thus, you can use nested case expression to check the month August
DECLARE #BILL_DATE datetime,
#sDate datetime,
#eDate datetime
SET #BILL_DATE = '20130815'
SET #sDate = DATEADD(MONTH, DATEDIFF(MONTH, 0, #BILL_DATE), 0)
SET #eDate = DATEADD(MONTH, 1, #sDate)
SELECT COUNT(CASE WHEN B.BILL_DATE >= #sDate AND B.BILL_DATE < #eDate
THEN CASE WHEN CAT_CODE='1'
THEN C.CONSUMER_NO END END) AS CAT_1_THIS_MONTH,
SUM(CASE WHEN CAT_CODE='1' THEN C.UNIT END) AS CAT_1_FROM_APRIL,
COUNT(CASE WHEN B.BILL_DATE >= #sDate AND B.BILL_DATE < #eDate
THEN CASE WHEN CAT_CODE='2'
THEN C.CONSUMER_NO END END) AS CAT_2_THIS_MONTH,
SUM(CASE WHEN CAT_CODE='2' THEN C.UNIT END) AS CAT_2_FROM_APRIL,
COUNT(CASE WHEN B.BILL_DATE >= #sDate AND B.BILL_DATE < #eDate
THEN CASE WHEN CAT_CODE='3'
THEN C.CONSUMER_NO END END) AS CAT_3_THIS_MONTH,
SUM(CASE WHEN CAT_CODE='3' THEN C.UNIT END) AS CAT_3_FROM_APRIL
FROM CONSUMER C INNER JOIN BILLING B ON C.CONSUMER_NO = B.CONSUMER_NO
WHERE B.BILL_DATE >= '20130401 00:00:00' AND B.BILL_DATE < #eDate
AND (more condition will be as per For Sample text 1)