I have a ballot where each voter gets 3 votes, choosing from 10 different candidates. Vote 1 is allocated 3 points, vote 2 gets 2 points and vote 3 gets 1 point.
I have the following SQL queries to total the number of points gained from each of the votes (so separate results for votes 1, 2 and 3).
What I need to do is put all these results together in a single table, but I'm not too sure where to start.
SELECT cn.cand_name, (count(vote_1) * 3) as vote_1 FROM candidate_votes cv Inner Join candidate_names cn ON cv.vote_1 = cn.cand_number GROUP BY cand_name;
SELECT cn.cand_name, (count(vote_2) * 2) as vote_2 FROM candidate_votes cv Inner Join candidate_names cn ON cv.vote_2 = cn.cand_number GROUP BY cand_name;
SELECT cn.cand_name, (count(vote_3) * 1) as vote_3 FROM candidate_votes cv Inner Join candidate_names cn ON cv.vote_3 = cn.cand_number GROUP BY cand_name;
I have the following results table:
Voter_number Vote_1 Vote2 Vote3
123 cand_1 cand_3 cand_2
456 cand_2 cand_1 cand_3
789 cand_2 cand_3 cand_1
And the following candidate name table:
cand_number cand_name
cand_1 Dave
cand_2 Sarah
cand_3 Nigel
So the results I'm looking for will look something like:
Candidate Votes
Dave 6
Sarah 7
Nigel 5
SELECT
cn.cand_name,
count(cv1.vote_1) * 3 as vote_1,
count(cv2.vote_2) * 2 as vote_2,
count(cv3.vote_3) as vote_3
FROM
candidate_names cn
LEFT JOIN
candidate_votes cv1 ON cv1.vote_1 = cn.cand_number
LEFT JOIN
candidate_votes cv2 ON cv2.vote_2 = cn.cand_number
LEFT JOIN
candidate_votes cv3 ON cv3.vote_3 = cn.cand_number
GROUP BY cn.cand_name;
This also allows you to add all votes
(count(cv1.vote_1) * 3) +
(count(cv2.vote_2) * 2) +
count(cv3.vote_3) as totalvotes
Edit: rows are being multiplied by the JOIN which is why it's wrong for cand2 and cand3
SELECT
cn.cand_name,
SUM(CASE WHEN cv.vote_1 = cn.cand_number THEN 3 ELSE 0 END) as vote_1,
SUM(CASE WHEN cv.vote_2 = cn.cand_number THEN 2 ELSE 0 END) as vote_2,
SUM(CASE WHEN cv.vote_3 = cn.cand_number THEN 1 ELSE 0 END) as vote_3
FROM
candidate_names cn
JOIN
candidate_votes cv ON cn.cand_number IN (cv.vote_1, cv.vote_2, cv.vote_3)
GROUP BY cn.cand_name;
SELECT cn.cand_name
, COALESCE(cv1.cnt_1,0)
, COALESCE(cv2.cnt_2,0)
, COALESCE(cv3.cnt_3,0)
, 3*COALESCE(cv1.cnt_1,0) + 2*COALESCE(cv2.cnt_2,0)
+ 1*COALESCE(cv3.cnt_3,0) AS total
FROM candidate_names AS cn
LEFT JOIN
( SELECT vote_1 AS vote
, COUNT(*) AS cnt_1
FROM candidate_votes cv
GROUP BY vote_1
) AS cv1
ON cv1.vote = cn.cand_number
LEFT JOIN
( SELECT vote_2 AS vote
, COUNT(*) AS cnt_2
FROM candidate_votes cv
GROUP BY vote_2
) AS cv2
ON cv2.vote = cn.cand_number
LEFT JOIN
( SELECT vote_3 AS vote
, COUNT(*) AS cnt_2
FROM candidate_votes cv
GROUP BY vote_3
) AS cv3
ON cv3.vote = cn.cand_number
SELECT
Candidate = n.cand_name,
Votes = SUM(s.vote_weight)
FROM (
SELECT
cand_number = CASE x.weight
WHEN 1 THEN Vote3
WHEN 2 THEN Vote2
WHEN 3 THEN Vote1
END,
vote_weight = x.weight
FROM candidate_votes v
CROSS JOIN (SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3) x (weight)
) s
INNER JOIN candidate_names n ON s.cand_number = n.cand_number
GROUP BY n.cand_name
Related
I am using T-SQL and aim to use three WITH clauses to collect into my main table query. If I run each WITH query by themselves, I can get the desired results. But when blending it into my main table query, no results show. I reckon my WITH clauses are okay but have played around with my main table query with no desired outcome.
Any help is much appreicated.
WITH n1 AS (SELECT m.name AS n1nom, SUM(y.Column1) AS SomeTotal1
FROM Mees m
INNER JOIN Listy y
ON y.m1=m.name
WHERE Yr=2020
GROUP BY m.name)
,
n2 AS (SELECT m.name AS n2nom, SUM(y.Column1) AS SomeTotal2
FROM Mees m
INNER JOIN Listy y
ON y.m2=m.name
WHERE Yr=2020
GROUP BY m.name)
,
n3 AS (SELECT m.name AS n3nom, SUM(y.Column1) AS SomeTotal3
FROM Mees m
INNER JOIN Listy y
ON y.m3=m.name
WHERE Yr=2020
GROUP BY m.name)
SELECT m.name, SUM(n1.sometotal1 + n2.sometotal2 + n3.sometotal3) AS Cool
FROM Mees M
INNER JOIN n1
ON n1.n1nom=m.name
INNER JOIN n2
ON n2.n2nom=m.name
INNER JOIN n3
ON n3.n3nom=m.name
GROUP BY m.name, n1.sometotal1 + n2.sometotal2 + n3.sometotal3;
If you want the total of y.Column1 when m.name matches any of y.m1, y.m2 and y.m3 then you need only 1 join and aggregation:
SELECT m.name, SUM(y.Column1) AS Total
FROM Mees m
INNER JOIN Listy y
ON m.name IN (y.m1, y.m2, y.m3)
WHERE Yr = 2020
GROUP BY m.name
If you want to add y.Column1 multiple times in case more than 1 of y.m1, y.m2 and y.m3 match m.name then use a CASE expression inside SUM():
SELECT m.name,
SUM(
(
CASE WHEN m.name = y.m1 THEN 1 ELSE 0 END +
CASE WHEN m.name = y.m2 THEN 1 ELSE 0 END +
CASE WHEN m.name = y.m3 THEN 1 ELSE 0 END
) * y.Column1
) AS Total
FROM Mees m
INNER JOIN Listy y
ON m.name IN (y.m1, y.m2, y.m3)
WHERE Yr = 2020
GROUP BY m.name
I need to divide two queries, but I need to save 'group by' categories. With my query I only get values and their cartesian product.
Select m2.regionname, m2.indicatorname CAST( m2.a2Value as float) /
m1.a1Value
from(
select r.name as regionname , ina.name as indicatorname, sum(a.value) as
a1Value
from Region as "r"
left join city_region as "cr" on r.region_id = cr.region_id
left join Office as "o" on cr.city_id = o.city_id
left join Assets as "a" on o.office_id = a.office_id
left join Indicators as "i" on a.indicator_id = i.indicator_id
left join IndicatorNames as "ina" on i.indicator_name_id =
ina.indicator__name_id
where a.month between '01-01-2019' and '31-01-2019'
group by r.name, ina.name
) m1 join (
select r.name as regionname , ina.name as indicatorname, sum(a.value) as
a2Value
from Region as "r"
left join city_region as "cr" on r.region_id = cr.region_id
left join Office as "o" on cr.city_id = o.city_id
left join Assets as "a" on o.office_id = a.office_id
left join Indicators as "i" on a.indicator_id = i.indicator_id
left join IndicatorNames as "ina" on i.indicator_name_id =
ina.indicator__name_id
where a.month between '01-02-2019' and '27-02-2019'
group by r.name, ina.name) m2 on m1.regionname = m2.regionname
I need to get 4 rows and 3 columns, that includes region_name, indicator_name and float value.
But I only cant get table with values
0,0482248520710059
0,0565972222222222
0,0665680473372781
0,078125
0,705627705627706
0,974025974025974
1,01875
1,03550295857988
1,18343195266272
1,21527777777778
1,38888888888889
1,40625
15,1515151515152
17,3160173160173
21,875
25
but that is wrong.
This condition in the ON clause:
on m1.regionname = m2.regionname
will join many unrelated rows.
You must set another condition like:
on m1.regionname = m2.regionname and m1.indicatorname = m2.indicatorname
try Something like this:
select *, case when a1Value=0 then null else cast(a2Value as float) / a1Value end Ratio
from (
select r.name as regionname , ina.name as indicatorname,
sum(case when a.month between '01-01-2019' and '31-01-2019' then a.value else 0 end) as a1Value,
sum(case when a.month between '01-02-2019' and '27-02-2019' then a.value else 0 end) as a2Value
from Region r
left join city_region cr on r.region_id = cr.region_id
left join Office o on cr.city_id = o.city_id
left join Assets a on o.office_id = a.office_id and a.month between '01-01-2019' and '27-02-2019'
left join Indicators i on a.indicator_id = i.indicator_id
left join IndicatorNames ina on i.indicator_name_id = ina.indicator__name_id
group by r.name, ina.name
) tmp
I have 3 tables as follows. I want to find balance
Table A
studentID Name
1 Bob
2 Sam
3 Sara
Table B
id studentID Credit
1 1 100
2 1 150
3 2 150
4 2 150
5 3 100
6 3 200
Table C
id studentID Amount Type
1 1 50 cash
2 1 120 card
3 2 100 cash
4 2 130 card
5 3 50 card
6 3 150 card
I want to get a result table as follows where Balance = Sum(credit) - sum(amount) where type = card.
result Table
studentID Name Credit Amount Balance
1 Bob 250 120 130
2 Sam 300 130 170
3 Sara 300 200 100
EDIT
According to his comment this is the query he has tried
select A.studentID,
A.Name,
ISNULL(SUM(B.Credit),0) as [Credit],
ISNULL(SUM(C.Amount),0) as [Amount],
ISNULL(SUM(B.Credit),0) - (select ISNULL(SUM(C.Amount),0) from C Group by C.studentID having C.Type='card' and C.studentID=A.studentID) as [balance]
from A
left outer join B on A.studentID = B.studentID
left outer join C on B.studentID = C.studentID
group by A.studentID
Handles students with no credit/amounts:
SELECT A.STUDENTID, A.[NAME]
, sum(B.CREDIT) AS [CREDIT]
, sum(C.AMOUNT) AS [AMOUNT]
, sum(B.CREDIT) - sum(C1.AMOUNT) AS [BALANCE]
FROM #TABLEA A
INNER JOIN (SELECT A.STUDENTID, isnull(SUM(B.CREDIT), 0) AS CREDIT FROM #TABLEA A LEFT JOIN #TABLEB B ON A.STUDENTID = B.STUDENTID GROUP BY A.STUDENTID) B ON A.STUDENTID = B.STUDENTID
INNER JOIN (SELECT A.STUDENTID, isnull(SUM(C.AMOUNT), 0) AS AMOUNT FROM #TABLEA A LEFT JOIN #TABLEC C ON A.STUDENTID = C.STUDENTID WHERE [TYPE] = 'CARD' GROUP BY A.STUDENTID) C ON B.STUDENTID = C.STUDENTID
INNER JOIN (SELECT A.STUDENTID, isnull(SUM(C.AMOUNT), 0) AS AMOUNT FROM #TABLEA A LEFT JOIN #TABLEC C ON A.STUDENTID = C.STUDENTID WHERE [TYPE] = 'CARD' GROUP BY A.STUDENTID) C1 ON B.STUDENTID = C1.STUDENTID
group by A.STUDENTID, A.[NAME]
This should work fine
SELECT
a.studentid,
a.Name,
sum(b.Credit) AS Credit,
ISNULL(c.Amount,0) AS Amount,
sum(b.Credit) - ISNULL(c.Amount,0) AS Balance
FROM a
INNER JOIN b ON a.studentid = b.StudentID
LEFT JOIN (select sum(amount) amount,studentid from c where type='card'
group by studentid) as c ON a.studentid = c.studentid
GROUP BY a.studentid, a.Name,c.Amount
SQL Fiddle Link
I haven't tried it. Please let me know.
SELECT A.studentID,A.Name,B.Credit,C.Amount,(Credit-Balance) as Amount from A, B, C where A.studentID=B.studentID=C.studentID AND B.id=C.id AND C.type="card";
This should work. It assumes all students in A are in B. The left join caters for the fact that not all may have a 'card' amount, and returns a 0 if they do not (otherwise a credit subtracted by a NULL will return NULL balance).
SELECT
a.StudentID,
a.Name,
sum(b.Credit) AS Credit,
ISNULL(sum(c.Amount),0) AS Amount,
sum(b.Credit) - ISNULL(sum(c.Amount),0) AS Balance
FROM tableA as a
INNER JOIN tableB as b ON a.StudentID = b.StudentID
LEFT JOIN tableC as c ON a.StudientID = c.StudentID AND c.[Type] = 'card'
GROUP BY a.StudentID, a.Name
This would be my start point. To be sure about the right calcs.
Then I'll start to refactor.
SELECT a.*,
(SELECT SUM(b.credit) FROM TableB b WHERE a.studentID = b.studentID) as Credit,
(SELECT SUM(c.amount) FROM TableC c WHERE a.studentID = c.studentID) as Amount,
((SELECT SUM(b.credit) FROM TableB b WHERE a.studentID = b.studentID)
-
(SELECT SUM(c.amount) FROM TableC c WHERE a.studentID = c.studentID)) as Balance
FROM TableA a;
I made a sqlFiddle: http://sqlfiddle.com/#!9/b86dc/7
You can try a query like below
See live demo
select A.*, credit, amount, balance= coalesce(credit,0) - coalesce(amount,0) from
[table A] A
left join
(
select
studentid,
credit= sum(credit)
from [table b] b
group by studentid
)B
on A.studentID= B.studentID
left join
(
select
studentid,
amount= sum(amount)
from [table C]
group by studentid
)C
on A.studentID=C.studentID
i/p
Id Name InsertBy UpdateBy
1 A 2 2
2 B 1 2
3 C 4 3
4 D 4 5
5 E 1 3
O/P(THE COUNT OF EMPLOYEE ID IN INSERT AND COUNT OF EMPID IN UPDATE)
Name InsertBy UpdateBy
A 2 0
B 1 2
C 0 2
D 2 0
E 0 1
It seems to you would require to do self join with separate query (Inserted, Updated) for safer.
SELECT
t.name, a.insertBy, b.updateBy
FROM table t
INNER JOIN
(
SELECT
t.id, count (t1.insertBy) insertBy
FROM table t
LEFT JOIN table t1 on t1.insertBy = t.id
GROUP BY t.id
)a on a.id = t.id
INNER JOIN
(
SELECT
t.id, count (t2.updateBy) updateBy
FROM table t
LEFT JOIN table t2 on t2.updateBy = t.id
GROUP BY t.id
)b on b.id = t.id
Let me edit with other approach which more efficient with separate join
select t1.name, sum(case when a.Name = 'InsertedBy' then 1 else 0 end) InsertBy,
sum(case when a.Name = 'UpdatedBy' then 1 else 0 end) UpdateBy
from table t
cross apply (
values (InsertBy, 'InsertedBy'), (UpdateBy, 'UpdatedBy')
)a(Types, Name)
LEFT JOIN table t1 on t1.Id = a.Types
group by t1.name
However, these will make use of index on (Id)
I am trying to get top 5 customertypes and show data for each 5 customer types, The balance (which can be any amount) I show them as "Other Customer Types". my issue is since the rows can be random and not perfectly divisible by a number then there can be repeated values in the top 5 showing up in the "Other" group which overstates the Total sales.
the Data is also being rendered in SSRS
My code using TOP PERCENT:
select final.[description], sum(final.YTDSales$) as YTDSales$
FROM(
select top 25 percent pytd2.[Description], sum(pytd2.YTDSales$) as YTDSales$
FROM(
-- ytd sales
select re.SIC_Desc as [description], sum((ol.NetAmt - ol.WhlOrdDiscAmt) / #exrt) AS YTDSales$
from dbo.order_line_invoice ol
INNER JOIN dbo.Vendor vd ON ol.Cono = vd.Cono AND vd.VendId = ol.VendId
inner join Product_Warehouse pw on ol.ProdId = pw.prodid and ol.WhseId = pw.whseid and ol.cono = pw.cono
inner join Customer c on ol.custId = c.CustId and ol.Cono = c.Cono
left join MDData.dbo.RetailEnvironment re on c.SIC = re.SIC
where ol.InvoiceDate BETWEEN #FStartDate AND #EndDate AND ol.Cono = 1 and ol.VendId IN(#Vendid) and ol.prodcatid NOT LIKE 'GP%'
group by re.SIC_Desc
)PYTD2
group by pytd2.[description]
order by sum(pytd2.YTDSales$) DESC
UNION ALL
select top 75 percent 'Other' as 'description', sum(pytd.YTDSales$) as YTDSales$
FROM(
-- ytd sales
select re.SIC_Desc as [description], sum((ol.NetAmt - ol.WhlOrdDiscAmt) / #exrt) AS YTDSales$
from dbo.order_line_invoice ol
INNER JOIN dbo.Vendor vd ON ol.Cono = vd.Cono AND vd.VendId = ol.VendId
inner join Product_Warehouse pw on ol.ProdId = pw.prodid and ol.WhseId = pw.whseid and ol.cono = pw.cono
inner join Customer c on ol.custId = c.CustId and ol.Cono = c.Cono
left join MDData.dbo.RetailEnvironment re on c.SIC = re.SIC
where ol.InvoiceDate BETWEEN #FStartDate AND #EndDate AND ol.Cono = 1 and ol.VendId IN(#Vendid) and ol.prodcatid NOT LIKE 'GP%'
group by re.SIC_Desc
)PYTD
group by Ppytd.[description]
order by sum(pytd.YTDSales$)
)final
group by final.[Description]
order by sum(final.YTDSales$) DESC
my results:
As you can see the Large Independent and Other has the same figure of $2280.60 in YTDQty since it is being repeated
I was picturing something like this:
with data as (
-- your base query here grouped and summarized by customer type
), rankedData as (
select *, row_number() over (order by YTDSales$ desc) as CustTypeRank
from data
)
select
case when CustTypeRank <= 5 then min("description") else 'Others' end as "description",
sum(YTDSales$) as YTDSales$
from rankedData
group by case when CustTypeRank <= 5 then CustTypeRank else 999 end
order by case when CustTypeRank <= 5 then CustTypeRank else 999 end
I actually used RANK instead which worked great :-
select 0 as rankytd, RANK() OVER(ORDER BY sum(ol.NetAmt - ol.WhlOrdDiscAmt) DESC) as rankpytd, re.sic, ol.VendId, vd.name, re.SIC_Desc As [description], 0 AS YTDQty, sum(ol.Quantity) AS PYTDQty
from dbo.order_line_invoice ol
INNER JOIN dbo.Vendor vd ON ol.Cono = vd.Cono AND vd.VendId = ol.VendId
inner join dbo.Product p on ol.Cono = p.Cono and ol.prodid = p.ProdId and p.ProdCatId in (#pcat)
inner join Product_Warehouse pw on ol.ProdId = pw.prodid and ol.WhseId = pw.whseid and ol.cono = pw.cono
inner join Customer c on ol.custId = c.CustId and ol.Cono = c.Cono
left join MDData.dbo.RetailEnvironment re on c.SIC = re.SIC
where ol.InvoiceDate BETWEEN DATEADD(YEAR, -1,#FStartDate) AND DATEADD(YEAR, -1, #EndDate) and ol.Cono = 1 and ol.VendId IN(#Vendid) and ol.prodcatid NOT LIKE 'GP%'
group by re.sic, ol.VendId, vd.Name, re.SIC_Desc