Creating a "pivot table" like construct in sql - sql

This is going to be a bit convoluted but I have a table named dbo.sizeclassreport. I apologize if this is a bit vague.
It has 78656 rows/records which is not all that immense. There are three salient fields; employment, area, and sizeclass. Sample data below
Area Employment sizeclass
000003 4 01
000001 6 02
000013 12 03
000003 2 01
000005 23 04
000001 7 02
000003 11 03
From here, the result would look this. There are 9 sizeclasses and 18 areas. Need to produce 9 breakdowns of each area. Each area would have a breakdown by size class.
Area Sizeclass Employment in Size Class Number of Sites
Total 00 17(6+11) 3
000003 01 6 (4+2) 2
000003 02 NULL or Empty NULL or Empty
000003 03 11 1
......
000003 09 NULL or Empty NULL or Empty

SELECT
[Areas].[Area],
[classSizes].[sizeclass],
SUM([data].[Employment]) AS [Employment in Size Class],
/*this would be better with your PK */
CASE COUNT([data].[Employment])
WHEN 0 THEN NULL
ELSE COUNT([data].[Employment])
END AS [Number of Sites]
/*These rows create your base matix*/
FROM (SELECT DISTINCT
[Area]
FROM [sizeclassreport]) AS [Areas]
CROSS JOIN (SELECT DISTINCT
[sizeclass]
FROM [sizeclassreport]) AS [classSizes]
/* this is where you select your data*/
LEFT OUTER JOIN [sizeclassreport] [data]
ON [Areas].[Area] = [data].[Area]
AND [classSizes].[sizeclass] = [data].[sizeclass]
GROUP BY
[areas].[Area],
[classSizes].[sizeclass]
ORDER BY
[areas].[Area],
[classSizes].[sizeclass]

It looks like you want all areas and sizeclasss in the output. If so, use cross join to generate the results, then left join and group by for the calculations:
select a.area, sc.sizeclass,
sum(scr.employment) as employment,
count(scr.area) as number_of_sizes
from (select distinct area from dbo.sizeclassreport scr) a cross join
(select distinct sizeclass from dbo.sizeclassreport scr) sc left join
dbo.sizeclassreport scr
on a.area = scr.area and sc.sizeclass = scr.sizeclass
group by a.area, sc.sizeclass;
Note: This returns 0 instead of NULL for the last column. This makes more sense to me. However, you could use NULLIF() if you really want a NULL value.

Related

Join records only on first match

im trying to join two tables. I only want the first matching row to be joined the others have to be null.
One of the tables contains daily records per User and the second table contains the goal for each user and day.
The joined result table should only join the firs ocurrence of User and Day and set the others to null. The Goal in the joined table can be interpreted as DailyGoal.
Example:
Table1 Table2
Id Day User Value Id Day User Goal
================================ ============================
01 01/01/2020 Bob 100 01 01/01/2020 Bob 300
02 01/01/2020 Bob 150 02 02/01/2020 Carl 170
03 01/01/2020 Bob 50
04 02/01/2020 Carl 200
05 02/01/2020 Carl 30
ResultTable
Day User Value Goal
============================================
01/01/2020 Bob 100 300
01/01/2020 Bob 150 (null)
01/01/2020 Bob 50 (null)
02/01/2020 Carl 200 170
02/01/2020 Carl 30 (null)
I tryed doing top1, distinct, subqueries but I cant find way to do it. Is this possible?
One option uses window functions:
select t1.*, t2.goal
from (
select t1.*,
row_number() over(partition by day, user order by id) as rn
from table1 t1
) t1
left join table2 t2 on t2.day = t1.day and t2.user = t1.user and t1.rn = 1
A case expression is even simpler:
select t1.*,
case when row_number() over(partition by day, user order by id) = 1
then t2.goal
end as goal
from table1 t1

Get SUM of Reference table rows

tblOrders
OrderId totalamount OrderStatus
01 1000 4
02 2000 4
tblCart
CartId OrderId NetPrice
05 01 400
06 01 650
07 02 750
08 02 1350
Desired Result:
OrderId totalamount OrderStatus NetPrice ItemCount
01 1000 4 1050 2
02 2000 4 2100 2
I want to achieve the JOIN of two tables with SUM(Netprice) based on OrderId Link
I tried doing this using SQL Query for a Stored Procedure but its giving ambiguous column error.
Please use below query,
select t1.OrderId, t1.totalamount, tl1.OrderStatus, sum(t2.NetPrice) as NetPrice ,
count(1) as ItemCount from
tblOrders t1
inner join tblCart t2
on (t1.OrderId = t2.OrderId)
group by t1.OrderId, t1.totalamount, tl1.OrderStatus;
If you want all columns from the orders table and summarized data from the other, you might find that apply is helpful:
select o.*, c.*
from tblOrders o outer apply
(select sum(c.netprice) as netprice, count(*) as itemcount
from tblCart c
where c.OrderId = o.OrderId
) c;

How to perform group function over an outer join query?

I have the following categories:
category_id name
----- -----
50D34E5A-A935-490A-9492-153DE50A94A2 Luxuries
013E3D0F-E755-495B-8D1E-4A3D1340ACF8 Household
88C477EE-CF99-49B4-9E92-4C41B09A5715 Petrol
40099E3A-18F1-4710-A803-7107648518CC Other
E3B81693-07B5-4D69-A3EC-796CA4290B45 Rent
F0728052-0733-454B-B8EE-96AB6D6E40BE Insurance
6E06581A-1643-4DEC-90B7-9D57F770F313 Groceries
CFD1ED67-7059-4A33-8DD6-F2FFAB213970 Monthly Bill
And the following transactions (shortened and joined on category_id):
category_id amount
------ -----
Luxuries 14
Household 14
Petrol 14
Other 14
Rent 14
Insurance 14
There are no Groceries transactions. I would like to sum these amounts and display their count but include Groceries in the results, but displaying zero. I have tried this:
SELECT SUM(ut.amount), COUNT(ut.amount), c.name
FROM User_Transaction ut
FULL OUTER JOIN Category c ON (ut.category_id = c.category_id)
GROUP BY c.name
total count name
--- --- ---
84 6 Household
84 6 Insurance
84 6 Luxuries
98 7 Monthly Bill
56 4 Other
182 13 Petrol
112 8 Rent
But Groceries has not been included. How can I include Groceries on the result set but just displaying as 0?
Use a LEFT JOIN starting with category (that values you want to keep):
SELECT c.name, COALESCE(SUM(ut.amount), 0) as amount,
COUNT(ut.category_id) as num_transactions,
FROM Category c LEFT JOIN
User_Transaction ut
ON ut.category_id = c.category_id
GROUP BY c.category_id, c.name;
That said, I think your query should do what you want, although it is misleading to use a full join in this context.

Not the right subquery, multple counties in a state trying to get count of state

SDRP15_COSD table
| ST_code | |SD Code| |County Code|
04 123 001
06 232 001
09 332 001
Submission table
| ST_code | |SD_Code| |Date|
04 123 01/21/2003
06 232 null
09 332 01/21/2003
I tried it this way, but I don't think it fulfills my requirements.
select st_code, count(sd_code)
from sdrp15_cosd
where sd_code in
(select sd_code from submission_table
where date is null)
group by st_code
order by st_code
The requirement here is the total count of each county listed in submission table for the corresponding state where the date field is null. Take into account that there are multiple counties in a state. and these tables are a simplified version compared to my 1000 rows.
Think i got it. What you need to do is join the tables by both st code and sd code, and then simply use where date is null and group by county code.
Hope this helps!
This query will give you the desired results i am assuming few things
st_code and sd_code are unique for particular country.
select count(1),country
from ex_9 a,ex_10 b
where a.st_code = b.st_code
and a.sd_code = b.sd_code
and b.data is null
group by country

SQL PIVOT ON Across Multiple Tables

I am trying to do a PIVOT (am running SQL Server 2008) across multiple tables and with no aggregate function involved. I have to be honest I'm a little out of my depth here and am struggling to define the problem so figure I should just jump in and show you my stuff (oooeeer), firstly I have three tables:
CHARTER_vessels
===============
vesselID vesselName
-------- ----------
1 The Titanic
2 The Pinafore
3 The Black Pearl
CHARTER_rateDateRange
=====================
rateDateRangeID rateDateRangeName
--------------- -----------------
1 Spring 2012
2 Summer 2012
3 Fall 2012
CHARTER_rates
=============
vesselID rateDateRangeID rateCost
-------- --------------- --------
1 1 434
1 2 445
1 3 231
2 1 675
2 2 545
2 3 768
3 1 543
3 2 654
3 3 658
And the output I'm trying to achieve is that the rates for each boat appear in the column for each season, like this:
vesselName Spring 2012 Summer 2012 Fall 2012
---------- ----------- ----------- ---------
The Titanic 434 445 231
The Pinafore 675 545 768
The Black Pearl 543 654 658
Obviously I would like to be able to sort the result set by the different columns if possible!
The below makes the assumption of uniqueness of vessel and date range. If this isn't true and you don't want to aggregate a pivot is not for you. The <aggregate>(rateCost) is a requirement to use a SQL Server pivot. There needs to be a mechanism for SQL Server to decide what to return if a vessel has multiple of the same daterange. If this doesn't occur the aggregate is really meaningless. The other option would be a series of self joins. Let me know if you need to see the self join solution.
SELECT src.vesselName,pvt.[Spring 2012], pvt.[Summer 2012], pvt.[Fall 2012]
FROM
(select vesselName, rateCost, rateDateRangeName
from CHARTER_rateDateRange crd
inner join CHARTER_rates cr on cr.rateDateRangeID = crd.rateDateRangeID
inner join CHARTER_vessels cv on cv.vesselID = crd.vesselID) AS src
PIVOT
(
max(rateCost)
FOR rateDateRangeName IN ([Spring 2012], [Summer 2012], [Fall 2012])
) AS pvt;
Ah why not in case someone else runs across this is the self join solution. Caution not at all optimized.
with joinMe as (
select vesselName, rateCost, rateDateRangeName
from CHARTER_rateDateRange crd
inner join CHARTER_rates cr on cr.rateDateRangeID = crd.rateDateRangeID
inner join CHARTER_vessels cv on cv.vesselID = crd.vesselID
)
select a.vesselName,a.rateCost as 'Spring 2012',b.rateCost as 'Summer 2012',c.rateCost as 'Fall 2012'
from joinMe a
inner join joinMe b on b.vesselName= a.vesselName
and b.rateDateRangeName = 'Summer 2012'
inner join joinMe c on c.cesselName = a.vesselName
and c.rateDateRangeName = 'Fall 2012'
where a.rateDateRangeName = 'Spring 2012'
Due to the size limit I will write a query response for you here. What does the following return for you anything with a count greater than 1?
select vesselName, rateDateRangeName,count(rateCost)
from CHARTER_rateDateRange crd
inner join CHARTER_rates cr on cr.rateDateRangeID = crd.rateDateRangeID
inner join CHARTER_vessels cv on cv.vesselID = cr.vesselID
group by vesselName,rateDateRangeName
order by count(rateCost) desc