SQL Query With Nested Sum - sql

I am having to write quite a complicated query at the moment but I am getting stuck. The table structure is as follows
Inquiry is linked to Timelog by a field called Inquiry_ID. My current code which brings back total minutes but for the entire table and not per company. What I am basically after:
Two Columns one for company name (dbo.inquiry.concom) and another for total minutes. The table INQUIRY holds say 100 entries for the same company, I want a row to return the company name once and the total amount of minutes counted for that company name from TIMELOG.LOGMINS
So for example there are 50 entries in dbo.inquiry that have the same company name, I want it to display a distinct company but I need it to total the amount of minutes that is in another table. I am completely lost!
DECLARE #StartDate DATETIME, #EndDate DATETIME
SET #StartDate = dateadd(mm, - 1, getdate())
SET #StartDate = dateadd(dd, datepart(dd, getdate()) * - 1, #StartDate)
SET #EndDate = dateadd(mm, 1, #StartDate)
SELECT DISTINCT TOP 100 PERCENT dbo.INQUIRY.CONCOM, TIMELOG_1.LOGMINS, dbo.INQUIRY.ESCDATE, dbo.INQUIRY.INQUIRY_ID,
(SELECT SUM(LOGMINS) AS Expr1
FROM dbo.TIMELOG
WHERE dbo.INQUIRY.ESCDATE BETWEEN #Startdate AND #EndDate) AS TOTALMINUTES
FROM dbo.INQUIRY INNER JOIN
dbo.TIMELOG AS TIMELOG_1 ON dbo.INQUIRY.INQUIRY_ID = TIMELOG_1.INQUIRY_ID INNER JOIN
dbo.PROD ON dbo.INQUIRY.PROD_ID = dbo.PROD.PROD_ID INNER JOIN
dbo.CATEGORY ON dbo.PROD.CATEGORY_ID = dbo.CATEGORY.CATEGORY_ID
WHERE dbo.INQUIRY.ESCDATE BETWEEN #Startdate AND #EndDate
ORDER BY dbo.INQUIRY.CONCOM
EDIT: The reason the category and product tables are there is because I will need to exclude the count based on whether a product is in a certain category.

SELECT i.concom, COALESCE(SUM(t.logmins), 0)
FROM inquiry i
LEFT JOIN
timelog t
ON t.inquiry_id = i.inquiry_id
GROUP BY
i.concom

Related

Don't have the answer to attached query?

My data is in two tables. The format of the two tables is below :
I had to figure out for all customers aged between 25 to 35 years find what is the net total revenue generated by these customers in last 30 days of transactions from max transaction date available in the data ?
I wrote below code
SELECT
TOP 1 YEAR(T2.TRAN_DATE)[TRAN_YEAR] ,MONTH(T2.TRAN_DATE)[TRAN_Month],
SUM(T2.Total_amt)[REVENUE]
FROM TRANSACTIONS T2
RIGHT JOIN CUSTOMER T1
ON T1.CUSTOMER_ID = T2.CUST_ID
WHERE DATEDIFF(YY, T1.DOB, GETDATE()) BETWEEN 25 AND 35
GROUP BY YEAR(T2.TRAN_DATE),MONTH(T2.TRAN_DATE)
ORDER BY YEAR(T2.TRAN_DATE) DESC, MONTH(T2.TRAN_DATE) DESC
My query works but when i calculated the same thing on excel it gave a different answer.
I am not able to figure out my mistake.
I am expecting a query like this:
SELECT SUM(T.Total_amt) as REVENUE]
FROM TRANSACTIONS T JOIN
CUSTOMER c
ON c.CUSTOMER_ID = t.CUST_ID
WHERE c.DOB >= DATEADD(YEAR, -35, GETDATE()) AND
c.DOB < DATEADD(YEAR, -24, GETDATE()) AND
t.TRAN_DATE > DATEADD(DAY, -30, GETDATE());
Note that this uses direct date comparisons rather than DATEDIFF(). These are usually more accurate.

How to Speed up SQL query for Date

I have a query, I'm using an inner join from 2 tables that have about a million rows. I'm trying to run the query so it only gets data from last month. However, it takes a really long time when using the getDate() function. But when I enter the date in this format '2016-12-01' and '2017-01-01' - it's really quick. How can I modify the query so it runs faster? I read that I might have to create a non-clustered index but I'm not really good with those yet.
select
custKey,
sum(salesAmt) as Sales,
sum(returnAmt) as Credit,
(sum(salesAmt) - sum(returnAmt)) as CONNET
from
[SpotFireStaging].[dbo].[tsoSalesAnalysis]
inner join
[SpotFireStaging].[dbo].OOGPLensDesc as o on tsoSalesAnalysis.ItemKey = O.ItemKey
where
PostDate between --DATEADD(MONTH, DATEDIFF(MONTH,0, GETDATE())-1,0 )
--AND DATEADD(MS, -3,DATEADD(MM, DATEDIFF(M,-1, GETDATE()) -1, 0))
'2016-12-01' and '2017-01-01'
group by
custkey
declare #startDate DateTime = DATEADD(MONTH, DATEDIFF(MONTH,0, GETDATE())-1,0 )
declare #endDate DateTime = DATEADD(MS, -3,DATEADD(MM, DATEDIFF(M,-1, GETDATE()) -1, 0))
select
custKey,
sum(salesAmt) as Sales,
sum(returnAmt) as Credit,
(sum(salesAmt) - sum(returnAmt)) as CONNET
from
[SpotFireStaging].[dbo].[tsoSalesAnalysis]
inner join
[SpotFireStaging].[dbo].OOGPLensDesc as o on tsoSalesAnalysis.ItemKey = O.ItemKey
where
PostDate between #startDate AND #endDate
group by
custkey
another alternative, check out the selected answer here:
When using GETDATE() in many places, is it better to use a variable?
GetDate() is calculated separately for each row, so we gotta belive so is DateDiff() and DateAdd(). So we are better off moving it into a local variable.

SQL Query to get oldest date

I'm new to SQL and have a large database that contains IDs and Service Dates and I need to write a query to give me the first date each ID had a service.
I tried:
SELECT dbo.table.ID, dbo.otherTable.ServiceDate AS EasliestDate
FROM dbo.table INNER JOIN dbo.table.ID = dbo.otherTable.ID
But the output is every service for every ID, which has too many results to sort through. I want the output to only show the ID and the oldest service date. Any advice is appreciated.
EDIT: To be more precise, the output I am looking for is the ID and service date if the oldest service date is during the year that I specify. I.E. if ID = 1 has a service in 2015 and 2016 and I am searching for IDs in 2016 then ID = 1 should not appear in the results because there was an earlier service in 2015.
EDIT: Thanks everyone who helped with this! The answer I accepted did exactly what I asked. Major kudos to Patty though who who elaborated on how to further filter the outcome by year.
Use GROUP BY and MIN to get the first date for each ID:
SELECT dbo.table.ID,
MIN(dbo.otherTable.ServiceDate) AS EasliestDate
FROM dbo.table
INNER JOIN otherTable
ON dbo.table.ID = dbo.otherTable.ID
GROUP BY dbo.table.ID;
ADDENDUM
In reference to a question in the comments:
how would I also restrict it to show only those who had a service in a specific year?
It would depend on your exact requirements, consider the following set:
ID ServiceDate
--------------------
1 2014-05-01
1 2015-08-01
1 2016-07-07
2 2015-08-19
You would only want to include ID = 1 if the year you specified was 2016, but assuming you still wanted to return the first date of 2014-05-01 then you would need to add a having clause with a case statement to get this.
DECLARE #Year INT = 2016;
DECLARE #YearStart DATE = DATEADD(YEAR, #Year - 1900, '19000101'),
#YearEnd DATE = DATEADD(YEAR, #Year - 1900 + 1, '19000101');
SELECT #YearStart, #YearEnd
SELECT t.ID,
MIN(o.ServiceDate) AS EasliestDate
FROM dbo.table AS t
INNER JOIN otherTable AS o
ON o.ID = r.ID
GROUP BY t.ID
HAVING COUNT(CASE WHEN o.ServiceDate >= #YearStart
AND o.ServiceDate < #YearEnd THEN 1 END) > 0;
If you only want the earliest date in 2016 the a where clause would suffice
DECLARE #Year INT = 2016;
DECLARE #YearStart DATE = DATEADD(YEAR, #Year - 1900, '19000101'),
#YearEnd DATE = DATEADD(YEAR, #Year - 1900 + 1, '19000101');
SELECT #YearStart, #YearEnd
SELECT t.ID,
MIN(o.ServiceDate) AS EasliestDate
FROM dbo.table AS t
INNER JOIN otherTable AS o
ON o.ID = r.ID
WHERE o.ServiceDate >= #YearStart
AND o.ServiceDate < #YearEnd
GROUP BY t.ID;
It is worth noting there is a very good reason I have chosen to calculate the start of the year, and the start of the next year and used
WHERE o.ServiceDate >= #YearStart
AND o.ServiceDate < #YearEnd
Instead of just
WHERE DATEPART(YEAR, o.ServiceDate) = 2016;
In the former, an index on ServiceDate can be used whereas in the latter, the DATEPART calculation must be done on every record and this can cause significant performace issues.
ADDENDUM 2
To do the following:
The exact thing I want then would be IDs who's earliest service is in the year I specify.
Then you would need a having clause, just a different one to the one I posted before:
DECLARE #Year INT = 2016;
DECLARE #YearStart DATE = DATEADD(YEAR, #Year - 1900, '19000101'),
#YearEnd DATE = DATEADD(YEAR, #Year - 1900 + 1, '19000101');
SELECT #YearStart, #YearEnd
SELECT t.ID,
MIN(o.ServiceDate) AS EasliestDate
FROM dbo.table AS t
INNER JOIN otherTable AS o
ON o.ID = r.ID
GROUP BY t.ID
HAVING MIN(o.ServiceDate) >= #YearStart
AND MIN(o.ServiceDate) < #YearEnd;
ADDENDUM 3
CREATE VIEW dbo.YourView
AS
SELECT dbo.table.ID,
MIN(dbo.otherTable.ServiceDate) AS EasliestDate
FROM dbo.table
INNER JOIN otherTable
ON dbo.table.ID = dbo.otherTable.ID
GROUP BY dbo.table.ID;
Then you can apply your criteria to the view:
SELECT *
FROM dbo.YourView
WHERE EasliestDate >= '2015-01-01'
AND EasliestDate < '2016-01-01';
You have to include a WHERE in your current query:
SELECT dbo.table.ID, dbo.otherTable.ServiceDate AS EasliestDate
FROM dbo.table INNER JOIN dbo.table.ID = dbo.otherTable.ID
WHERE Month(dbo.otherTable.ServiceDate) = 1
Or you can search with Year(dbo.otherTable.ServiceDate) = 2016
Or you can use Day(dbo.otherTable.ServiceDate) = 1
Or an specific date.
use group by and min to get records. Else you can refer http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/ for better understanding.
You need to use a "Group by" statement. Try this:
SELECT dbo.table.ID, Max(dbo.otherTable.ServiceDate) AS LatestDate, Min(dbo.otherTable.ServiceDate as EarliestDate)
FROM dbo.table INNER JOIN dbo.table.ID = dbo.otherTable.ID
group by dbo.table.ID
Use nested statement to get the min date , and then just match based on ID.
select t1.ID from table1 t1 INNER JOIN
(
SELECT ID, MIN(servicedate) MinServiceDate
FROM table2
GROUP BY ID
) t2 ON t1.ID = t2.ID

How to group by month and year for entire calender year until this month in SQL

I have this query
DECLARE #DATE datetime
SELECT #Date = '2014-04-01'
SELECT #Date, COUNT(*) FROM Claim C
INNER JOIN Prop_Vehicles PV ON PV.Prop = C.Prop
WHERE PV.Vehicle IN (1,2) AND
C.DateCreate >= #DATE AND
ClaimCodeId =5
I want to group by month wise for the calnder year. For example
April 2014 - 200
May 2014 - 300
June 2014 - 500
.
.
october 2014 - 100
something like this. How to achieve it? Could someone help me how to split #Date into two fields and also group by month year wise until current month like I mentioned above?
I reckon datepart function would do? Let me also check that.
Thank you in advance.
In case some months don't have data then this would skip those months.
If you want all months data even if value is zero, then you need to construct months table and join with it
SELECT DATEADD(MONTH, DATEDIFF(MONTH,0,C.DateCreate), 0), COUNT(*) FROM Claim C
INNER JOIN Prop_Vehicles PV ON PV.Prop = C.Prop
and PV.Vehicle IN (1,2) AND
and C.DateCreate >= #DATE AND
AND ClaimCodeId =5
group by DATEADD(MONTH, DATEDIFF(MONTH,0,C.DateCreate), 0)
as per latest comment
here is the way to get all months data and also to display year and month
DECLARE #DATE datetime
SELECT #Date = '2014-04-01'
;with cte
as
(
select DATEADD(month, datediff(month,0,#Date), 0) as monthVal,1 as N
union all
select DATEADD(month, datediff(month,0,#Date)+N, 0) as monthVal, N+1
FROM cte
WHERE n <=5
)
SELECT DATENAME(year, monthval) as Year, datename(month,monthVal) as Month, COUNT(*) FROM
cte
left join Claim C
on DATEADD(month, datediff(month,0,C.DAteCreate)= cte.monthVal
INNER JOIN Prop_Vehicles PV ON PV.Prop = C.Prop
and PV.Vehicle IN (1,2) AND
and C.DateCreate >= #DATE AND
AND ClaimCodeId =5
group by DATENAME(year, monthval) , datename(month,monthVal)

MSSQL select count where condition is met across a date range

I have a table containing date, employeeID(int) , and ShiftWorked (can be night/day/weekend or evening) . There is a row for each employee and date combination
I would like to construct a query that gives me a count of how many people have worked a night shift in the week before and after each date in the roster period.
--------------------------------------------------------------------------
Date (yyyy-MM-dd) | CountOfNightshifts(for 1 week either side of date)
--------------------------------------------------------------------------
2012-1-1 | 8
2012-1-2 | 12
2012-1-3 | 11
2012-1-4 | 6
etc | etc
I hope this is clear. I have spent days trying to get this to work but I am not getting anywhere.
For example:
SELECT COUNT(id), [date]
FROM ROSTER
WHERE Shift = night AND [date] BETWEEN DATEADD(D,-7,[date]) AND DATEADD(d,7,[date])
GROUP by [date]
group by [date]
This will give me a list of dates and a count of nights on that particular day - not all night shifts in the 7 days before and after the date.
The following query will return two columns: the reference (roster) date and the number of (distinct) people that have worked on the night sift seven days before to seven days after the reference date.
SELECT tmain.date,
(
SELECT COUNT(DISTINCT taux.employeeId)
FROM roster taux
WHERE taux.shiftWorked = 'night'
AND taux.date >= DATEADD(DAY, -7, tmain.date)
AND taux.date <= DATEADD(DAY, 7, tmain.date)
) AS [number_of_distinct_people_with_night_shift]
FROM roster tmain
ORDER BY tmain.date;
Note 1: Usually I prefer joins over sub-queries, but I guess this solution is easier to read.
Note 2: I am assuming the time component of date values are irrelevant and all dates have the same time (i.e. '00:00:00.00'); if it is not the case, there are more adjustments to be done on the date comparison.
how about this?
SELECT
[date]
,count(*)
FROM
Shifts as s
WHERE
s.Date > DATEADD(day,-7,GETDATE())
AND ShiftWorked = 'Night'
GROUP BY
date
http://sqlfiddle.com/#!3/e88cc/1
a bit more data:
http://sqlfiddle.com/#!3/b7793/2
If you are only interested in a specific date then you could use:
DECLARE #target datetime
SET #target = GETDATE()
SELECT
count(*) as NightShifts
FROM
Shifts as s
WHERE
ShiftWorked = 'Night'
AND s.Date > DATEADD(day,-7,#target)
AND s.Date < DATEADD(day,7,#target)
http://sqlfiddle.com/#!3/b7793/20
but if you have another table that actually has the periods in it (e.g. billing or payroll dates):
DECLARE #target datetime
SET #target = GETDATE()
SELECT
p.periodDate
,count(*)
FROM
Shifts as s
INNER JOIN periods as p
ON s.date > dateadd(day,-7,p.periodDate)
AND s.date < dateadd(day,7,p.periodDate)
WHERE
ShiftWorked = 'Night'
GROUP BY p.periodDate
http://sqlfiddle.com/#!3/fc54d/2
OR to get ) when no night shift was worked:
SELECT
p.periodDate
,ISNULL(t.num,0) as nightShifts
FROM
periods as p
LEFT OUTER JOIN (
SELECT
p.periodDate
,count(*) as num
FROM
Shifts as s
INNER JOIN periods as P
ON s.date > dateadd(day,-7,p.periodDate)
AND s.date < dateadd(day,7,p.periodDate)
WHERE
ShiftWorked = 'Night'
GROUP BY p.periodDate
) as t
ON p.periodDate = t.periodDate
http://sqlfiddle.com/#!3/fc54d/11
You can pull it off by joining the ROSTER table to itself, thereby creating several result rows per employee and day. Otherwise your GROUP BY clause will group the resulting rows from the period you are after into the dates of the original table.
SELECT
r.[date],
COUNT(period.id)
FROM ROSTER r
JOIN ROSTER period
ON period.employeeID=r.employeeID
AND period.shift = night
AND r.[date] BETWEEN DATEADD(d,-7,period.[date]) and DATEADD(d,7,period.[date])
WHERE
r.shift = night
GROUP BY r.[date]