SQL not grouping properly - sql

I am trying to find the number of records for certain service codes, by year - in my database.
The code:
SELECT datepart( year,dbo.PUBACC_HD.grant_date) as'Year',
dbo.PUBACC_HD.radio_service_code as 'Service Code',
count(dbo.PUBACC_FR.transmitter_make) as 'Number of Records'
FROM dbo.PUBACC_FR
INNER JOIN dbo.PUBACC_HD
ON dbo.PUBACC_FR.unique_system_identifier = dbo.PUBACC_HD.unique_system_identifier
GROUP BY dbo.PUBACC_HD.grant_date, dbo.PUBACC_HD.radio_service_code
ORDER BY [Number of Records] desc
Current Result:
Year Service Code Number of Records
----------- ------------ -----------------
2011 CF 11195 <----
2013 CF 2042
2011 CF 1893 <----
2013 CF 1879
2013 CF 1841
2013 CF 1741
2013 CF 1644
2010 CF 1595
2013 MG 1563
2011 CF 1512 <----
2013 CF 1510
2011 CF 1454
2011 CF 1428
2016 CF 1385
2011 CF 1378
2015 MG 1349
I want all of the fields to be aggregated. Example of none aggregations denoted by arrows. (2011, CF) is just one example in the large table of things not aggregating correctly.
Anyone know why this is happening?

You should use:
GROUP BY datepart( year,dbo.PUBACC_HD.grant_date)
instead of:
GROUP BY dbo.PUBACC_HD.grant_date
As it is right now, you are grouping by a date value, that may differ among records sharing the same radio_service_code value.

Change Group By To:
Group By datepart( year,dbo.PUBACC_HD.grant_date),dbo.PUBACC_HD.radio_service_code
You have only select year from grant_date, so you have to also write group by accordingly

Because you are grouping by grant_date and not by year

Try with statement and also change your group by condition.
;With CTE AS
(
SELECT
datepart( year,dbo.PUBACC_HD.grant_date) as Year,
dbo.PUBACC_HD.radio_service_code as ServiceCode,
count(dbo.PUBACC_FR.transmitter_make) as NumberofRecords
FROM dbo.PUBACC_FR
INNER JOIN dbo.PUBACC_HD
ON dbo.PUBACC_FR.unique_system_identifier = dbo.PUBACC_HD.unique_system_identifier
)
Select * from cte
GROUP BY Year, ServiceCode
ORDER BY NumberofRecords desc
As #Lucas Kot-Zaniewski state you are using year in select and group it by date, thats is problem.

Related

I want to calculate a percentage change within a column using SQL Server

Forgive me if this has already been addressed elsewhere, I checked but couldn't work for me. I am not very conversant with SQL Server.
I want to calculate the percentage of indices between months Jan - Dec denoted by 01, 02, 03... in my table using SQL Server. I have added what is expected in the column Pct_Change using Excel. See sample below:
M Year Indices Pct_Change
01 2017 190.51
02 2017 188.99 -0.8
03 2017 190.06 0.6
04 2017 194.24 2.2
05 2017 196.83 1.3
06 2017 196.30 -0.3
07 2017 191.09 -2.7
08 2017 190.42 -0.3
09 2017 194.02 1.9
10 2017 201.20 3.7
11 2017 200.98 -0.1
12 2017 194.43 -3.3
01 2018 197.23 1.4
02 2018 198.20 0.5
03 2018 194.60 -1.8
Any help will be greatly appreciated.
This is a guess, due to the lack of expected results, but perhaps...
WITH VTE AS(
SELECT *
FROM (VALUES(01,2017,190.51),
(02,2017,188.99),
(03,2017,190.06),
(04,2017,194.24),
(05,2017,196.83),
(06,2017,196.30),
(07,2017,191.09),
(08,2017,190.42),
(09,2017,194.02),
(10,2017,201.20),
(11,2017,200.98),
(12,2017,194.43),
(01,2018,197.23),
(02,2018,198.20),
(03,2018,194.60)) V(M, [Year], Indices))
SELECT *,
(Indices / LAG(Indices) OVER (ORDER BY [Year] ASC, M ASC)) -1 AS Pct_change
FROM VTE;
Edit: OP is, unfortunately, using 2008R2. Personally, I strongly suggest looking at upgrading your version. SQL Server 2008(R2) has less than a year of extended support left now. After that, you will receive no updates for it, including security updates. If you need to be GDPR compliant, then it's a must that you change.
Anyway, you can do this in SQL Server 2008R2 by using a LEFT JOIN to the same table:
WITH VTE AS(
SELECT *
FROM (VALUES(01,2017,190.51),
(02,2017,188.99),
(03,2017,190.06),
(04,2017,194.24),
(05,2017,196.83),
(06,2017,196.30),
(07,2017,191.09),
(08,2017,190.42),
(09,2017,194.02),
(10,2017,201.20),
(11,2017,200.98),
(12,2017,194.43),
(01,2018,197.23),
(02,2018,198.20),
(03,2018,194.60)) V(M, [Year], Indices)),
--The solution. note that you would need your WITH on the next line
--Mine isn't, as I used a CTE to create the sample data.
RNs AS(
SELECT *,
ROW_NUMBER() OVER (ORDER BY [Year] ASC, M ASC) AS RN
FROM VTE)
SELECT R1.M,
R1.[Year],
R1.Indices,,
(R1.Indices / R2.Indices) -1 AS Pct_change
FROM RNs R1
LEFT JOIN RNs R2 ON R1.RN = R2.RN + 1;

SQL Server 2012 - find duplicate month (string) but different year

Having difficulty getting my head around this one.
I've been asked to create a report showing customers who signed up in the same month in previous year.
Invoice table looks a bit like this: (can't figure out how to create a nicer table)
invoiceid customerid monthinvoice yearinvoice
1 50 July 2016*
2 51 July 2016
3 52 July 2016*
4 53 July 2016
5 54 August 2016
6 50 July 2017*
7 51 August 2017
8 52 July 2017*
9 53 August 2017
10 54 September 2017
The only proper date column used is date the invoice was generated and the date payment received.
The records marked with * are the ones I'm only interested in, I just want to see 2 records returned when I pass a month as a parameter (I'll be asked to show how many customers have renewed in August for example. If the 1st invoice was in July 2016 and next invoice in August 2017 they will be treated as a new customer, not a renewal (must be exactly 12 months))
1) 50
2) 52
Any help much appreciated.
Here is one way. First we get all invoices for this month, current year, then union to the same month of the previous year. Then, we filter on customers who have a record for both using HAVING.
;with cte as(
select *
from yourtable
where
(monthinvoice = #monthinvoice
and yearinvoice = datepart(year,getdate()))
union
select *
from yourtable
where
(monthinvoice = #monthinvoice
and yearinvoice = datepart(year,dateadd(year,-1,getdate()))))
select *
from cte
where customerid in (select customerid from cte group by customerid having count(invoiceid) > 1)
I think this should do the trick for you-
SELECT I1.invoiceid, I1.customerid, I1.monthinvoice, I1.yearinvoice, I2.yearinvoice
FROM Invoice_table I1
INNER JOIN Invoice table I2
ON I1.customerid = I2.customerid
AND I1.monthinvoice = I2.monthinvoice
AND I1.yearinvoice = I2.yearinvoice + 1
something like this
select customerid , monthinvoice from yourtable
where yearinvoice in (2016, 2017) and monthinvoice = 'July'
group by customerid , monthinvoice
having count(*) = 2
Something like the following should give you some ideas as to how to build the report out.
Declare #ReportYear as int = 2017;
--this should show all customers with invioices for these months in both 2017 and 2016
select a.customerid, a.monthinvoice
from
(
--get people with invoice last year
Select distinct customerid, monthinvoice
from Invoices i0
where yearinvoice = #ReportYear - 1
) a
join
(
--get people with invoice this year
Select distinct customerid, monthinvoice
from Invoices i0
where yearinvoice = #ReportYear
) b on a.customerid = b.customerid
and a.monthinvoice = b.monthinvoice
If Im following your question correctly...
SELECT customerid FROM InvTblName T
INNER JOIN (SELECT customerID
FROM InvTblName
HAVING Z.invyear=T.invyear+1) Z
ON T.invmonth=Z.invmonth

How do I get a 0 sum when grouping by more than one field?

I have tried looking and found using left outer joins tends to be the answer, but that usually is for a sum to a date grouping and not 2 levels of groupings. I think the additional grouping is messing me up.
My Table:
dbo._Labour
_id int pk --the id of this labour entry
_date date --the date of this entry (using this to group by year)
_activityid int --what type of activity they are doing
_typeid int --3 possible options (0,1,2) of what type of item they are performing the activity on
_hours numeric(4,0) --how many hours were spent performing that activity.
My ultimate goal is to know for each year the total hours spent working for each of the 3 types (0,1,2). My current code:
select coalesce(SUM(_hours),0) as _hours, YEAR(_date) as _year, l._typeid
from RelayTanks.dbo._Labour l
left outer join (select YEAR(_date) as _y
from RelayTanks.dbo._Labour group by YEAR(_date)) y on y._y = YEAR(l._date)
where _activityid in (3,4,9,11)
group by YEAR(_date), l._typeid
order by year(_date), l._typeid'
which results in:
_typeid _year _hours
0 2015 1174
1 2015 3953
2 2015 851
0 2016 119
1 2016 541
2 2016 65
1 2017 10
What I am looking for is 2017 to show _hours 0 for _typeid 0 and a 0 for _typeid 2. ie:
_typeid _year _hours
0 2015 1174
1 2015 3953
2 2015 851
0 2016 119
1 2016 541
2 2016 65
0 2017 0
1 2017 10
2 2017 0
I have tried many things including 2 outer left joins and I just can't quite figure it out. The current out join I thought would be a good substitute to actually having a dummy year table and only showing years where at least some work has been done.
This is my first question posted here so many apologies if I have neglected to include some info or am missing some community etiquette.
Thanks for your help!
This would get you there, assuming that all of the possible years and types are already represented in your data:
SELECT year(_date), _typeid, _hours = SUM(_hours)
FROM _Labour
GROUP BY year(_date), _typeid
UNION
SELECT year(y._date), t._typeid, 0
FROM _Labour y, _Labour t
WHERE NOT EXISTS (SELECT 1 FROM _Labour WHERE year(_Labour._date) = year(y._date) AND _Labour._typeid = t._typeid)
ORDER BY 1, 2
if you have to do this with just one table then something like this might work.
SELECT s._typeid,
s._y,
coalesce(SUM(lj._hours),0) as _hours
FROM
(
SELECT t._typeid, y._y
FROM (SELECT DISTINCT _typeid FROM RelayTanks.dbo._Labour) t
CROSS JOIN
(SELECT DISTINCT YEAR(_date) _y FROM RelayTanks.dbo._Labour) y
) s
LEFT JOIN RelayTanks.dbo._Labour lj ON lj._typeid = s._typeid AND YEAR(lj._date) = s._y
performance might be terrible.. you might want to consider adding a computed column for the _date.year value

Why doesn't my stored procedure work for all records?

I have a procedure which returns records correctly being sorted desc by JobStatus i.e.
3 = Current
2= Previous
1= Initial
i.e. record with status 3 is at top, with status 2 is after 3 and then 1. It works but I want to put another ORDERING filter too but can't figure out. The problem is the records with status 2 should be sorted according to FROM-TO Dates.
SP:
SELECT serviceinfo.pk_serviceinfo_serviceinfoid,
serviceinfo.fk_districts_serviceinfo_initialdistrictid,
serviceinfo.fk_personalinfo_serviceinfo_pid,
serviceinfo.fk_webusers_serviceinfo_userid,
serviceinfo.serviceinfoentrydatetime,
CONVERT(VARCHAR, serviceinfo.serviceinfofromdate, 106) AS
ServiceInfoFromDate,
serviceinfodepartment,
CASE serviceinfo.serviceinfotodate
WHEN '1900-01-01' THEN ''
ELSE CONVERT(VARCHAR, serviceinfo.serviceinfotodate, 106)
END ServiceInfoToDate,
serviceinfo.serviceinfoinitialbps,
serviceinfo.serviceinfoinitialdesignation,
serviceinfo.serviceinfoinitialbps,
personalinfo.personalinfoname,
districts.districtname,
serviceinfo.serviceinfojobstatus,
serviceinfo.serviceinfooffice,
serviceinfo.serviceinfocadre,
jobstatuses.statusname,
CASE serviceinfo.serviceinfoservicetype
WHEN 1 THEN 'Permanent'
WHEN 2 THEN 'Fixed Pay'
WHEN 3 THEN 'Contract'
END AS
ServiceInfoServiceType
FROM serviceinfo
LEFT JOIN districts
ON districts.pk_districts_districtid =
serviceinfo.fk_districts_serviceinfo_initialdistrictid
INNER JOIN personalinfo
ON personalinfo.pk_personalinfo_id =
serviceinfo.fk_personalinfo_serviceinfo_pid
LEFT JOIN jobstatuses
ON jobstatuses.pk_jobstatuses_jobstatusid =
serviceinfo.serviceinfojobstatus
INNER JOIN web_users
ON web_users.userid = serviceinfo.fk_webusers_serviceinfo_userid
WHERE serviceinfo.fk_personalinfo_serviceinfo_pid = #pk_PersonalInfo_ID
ORDER BY serviceinfo.serviceinfojobstatus DESC
Should be arranged according to ServiceInfoFromDate and ServiceInfoToDate
29 Dec 2015 15 Jan 2016 Current
14 Jan 2016 06 Feb 2016 Previous
06 Feb 2016 09 Apr 2016 Previous
08 Jan 2016 13 Jan 2016 Initial
the reocrds with status Previous is not arranged according to date
You can add additional query order by fields in a case statement:
ORDER BY serviceinfo.serviceinfojobstatus DESC,
case when serviceinfo.serviceinfojobstatus= 2 then ServiceInfoFromDate end
You didn't mention the exact problem you run into, but you should try this:
ORDER BY serviceinfo.serviceinfojobstatus DESC
, serviceinfo.serviceinfofromdate
, serviceinfo.serviceinfotodate
This will sort on the source attributes, not converted to varchar, as in your query. Which I think is what's preferable.

SQL query for displaying 3 fields of data using only one integer data entry

I have two separate csv tables with automated data that can be added to via another webpage. This is not static data, it is dynamic and changeable.
Here is my objective:
Write a web page showing a table of sales (Volume and Price) of each product for a single year. The user must be able to select the year.
So far with my query I've gotten this far:
SELECT Products.Name, SUM(MonthlySales.SalesVolume) as 'Total Sales'
FROM Products
INNER JOIN MonthlySales
ON Products.Id=MonthlySales.Id
GROUP BY Products.Name;
On top of that I need to be able to pass in a value (year) with php (which I am capable of doing) to further narrow the search. Am I going about this the right way?
Example Data:
MonthlySales.csv
Id ProductCode Month Year SalesVolume
23041 121 1 1980 983
23042 121 2 1980 960
23043 121 3 1980 939
23044 121 4 1980 927
23045 121 5 1980 931
23046 121 6 1980 950
23047 121 7 1980 975
Products.csv
Id,Name,Price
121,Jelly beans,6.79
122,Banana milkshake powder,8.31
123,Edam Cheese,18.73
124,Hairnet,8.05
125,Aubergine jam,2.66
By the end I need something like this displayed:
You selected Year: 1980
Name Price Volume
Jelly Beans 6.79 11000
Banana milkshake powder 8.31 15000
Any help would be much appreciated.
What you have will work, so long that Name is unique, but it's probably not the best way to do is. This might be more accurate, since you will be basing the group by the PK Id rather than the Name field.
DECLARE #year char(4) = /*your year - datatype should be same as your year col*/;
DECLARE #month int = /*your month - datatype should be same as your month col*/;
SELECT Products.Name, Products.Price, Sales.totalSales AS 'Total Sales'
FROM Products
INNER JOIN (
SELECT MonthlySales.ProductCode,
SUM(MonthlySales.SalesVolume) AS 'totalSales'
FROM MonthlySales
WHERE MonthlySales.Year = #year AND MonthlySales.Month = #month
GROUP BY MonthlySales.ProductCode
) AS Sales ON Sales.Id = Products.ProductCode