CALCULATE PERCENT BY DEVIDING AGREGATED VALUE ON TOTAL - sql

I'm trying to write a query that aggregates a column by a category and then find a percent of that category from total categories
WITH USERS_ENERGY AS
(SELECT D.REGION_ID,E.YEAR_DESC,HOME_TYPE_ID,OWNERSHIP_TYPE_ID,B.ELECTRICITY_TYPE_KEY,SUM(WEIGHT_FINAL) HH_BY_ELECTRICITY_TYPE
FROM
R_FACT_HOUSING_UNIT B
JOIN
RR_DIM_SAMPLE C ON B.SAMPLE_FORM_ID=C.SAMPLE_FORM_ID
JOIN
R_DIM_PLACES D ON C.FRAM_PLACE_ID=D.PLACE_ID
JOIN
R_DIM_YEAR E ON B.ROUND_YEAR=E.ID
JOIN
R_DIM_HOME_TYPE F ON F.HOME_TYPE_ID=B.HOME_TYPE_KEY
JOIN
R_DIM_OWNERSHIP_TYPE G ON G.OWNERSHIP_TYPE_ID=B.OWNERSHIP_TYPE_KEY
WHERE B.ELECTRICITY_TYPE_KEY IN(8200002,8200001)
GROUP BY D.REGION_ID,E.YEAR_DESC,HOME_TYPE_ID,OWNERSHIP_TYPE_ID,B.ELECTRICITY_TYPE_KEY),
ALL_HH AS
(
SELECT REGION_ID,YEAR_DESC,HOME_TYPE_ID,OWNERSHIP_TYPE_ID,SUM(B.WEIGHT_FINAL) TOTAL_HH
FROM
R_FACT_HOUSING_UNIT B
JOIN
RR_DIM_SAMPLE C ON B.SAMPLE_FORM_ID=C.SAMPLE_FORM_ID
JOIN
R_DIM_PLACES D ON C.FRAM_PLACE_ID=D.PLACE_ID
JOIN
R_DIM_YEAR E ON B.ROUND_YEAR=E.ID
JOIN
R_DIM_HOME_TYPE F ON F.HOME_TYPE_ID=B.HOME_TYPE_KEY
JOIN
R_DIM_OWNERSHIP_TYPE G ON G.OWNERSHIP_TYPE_ID=B.OWNERSHIP_TYPE_KEY
WHERE B.ELECTRICITY_TYPE_KEY IN(8200002,8200001)
GROUP BY REGION_ID,YEAR_DESC,HOME_TYPE_ID,OWNERSHIP_TYPE_ID
)
SELECT A."REGION_ID",A."YEAR_DESC",A."HOME_TYPE_ID",A."OWNERSHIP_TYPE_ID",A."ELECTRICITY_TYPE_KEY",A."HH_BY_ELECTRICITY_TYPE",TOTAL_HH FROM USERS_ENERGY A,ALL_HH B WHERE A.REGION_ID=B.REGION_ID AND A.YEAR_DESC=B.YEAR_DESC AND A.HOME_TYPE_ID=B.HOME_TYPE_ID AND A.OWNERSHIP_TYPE_ID=B.OWNERSHIP_TYPE_ID
that was the view I created.
when I tried to test it gave me wrong results
this was my test
SELECT ELECTRICITY_TYPE_KEY,SUM(HH_BY_ELECTRICITY_TYPE),SUM(TOTAL_HH)FROM MASDAR_HEN_3_A_A T
GROUP BY ELECTRICITY_TYPE_KEY
ORDER BY ELECTRICITY_TYPE_KEY
the result was:
but it supposes to be:
so if you devide values on total and add them it supposes to give 100%

Your query is almost impossible to follow. Some helpful advice for your query-writing:
Always use MEANINGFUL table alases, such as y for dim_year. Arbitrary letters are hard to follow.
Always use proper, explicit, standard JOIN syntax. Never use commas in the FROM clause.
As for your view, I think you just need analytic functions. A simpler version (assuming I haven't make mistakes on the table aliases):
SELECT p.REGION_ID, y.YEAR_DESC, ht.HOME_TYPE_ID, ot.OWNERSHIP_TYPE_ID, hu.ELECTRICITY_TYPE_KEY,
SUM(WEIGHT_FINAL) as HH_BY_ELECTRICITY_TYPE
SUM(SUM(WEIGHT_FINAL)) OVER (REGION_ID,YEAR_DESC, HOME_TYPE_ID, OWNERSHIP_TYPE_ID) as TOTAL_HH
FROM R_FACT_HOUSING_UNIT hu JOIN
RR_DIM_SAMPLE
ON hu.SAMPLE_FORM_ID = s.SAMPLE_FORM_ID JOIN
R_DIM_PLACES p
ON s.FRAM_PLACE_ID= p.PLACE_ID JOIN
R_DIM_YEAR y
ON hu.ROUND_YEAR = y.ID JOIN
R_DIM_HOME_TYPE ht
ON ht.HOME_TYPE_ID = hu.HOME_TYPE_KEY JOIN
R_DIM_OWNERSHIP_TYPE ot
ON ot.OWNERSHIP_TYPE_ID= hu.OWNERSHIP_TYPE_KEY
WHERE hu.ELECTRICITY_TYPE_KEY IN (8200002, 8200001)
GROUP BY p.REGION_ID, y.YEAR_DESC, ht.HOME_TYPE_ID, ot.OWNERSHIP_TYPE_ID, hu.ELECTRICITY_TYPE_KEY;

Oracle provides a RATIO_TO_REPORT analytic function to compute % of total for you.
SELECT
d.region_id,
e.year_desc,
home_type_id,
ownership_type_id,
b.electricity_type_key,
SUM(weight_final) hh_by_electricity_type,
100*RATIO_TO_REPORT(SUM(weight_final) OVER ( PARTITION BY REGION_ID,YEAR_DESC, HOME_TYPE_ID, OWNERSHIP_TYPE_ID ) pct_of_total
FROM
r_fact_housing_unit b
JOIN rr_dim_sample c ON b.sample_form_id = c.sample_form_id
JOIN r_dim_places d ON c.fram_place_id = d.place_id
JOIN r_dim_year e ON b.round_year = e.id
JOIN r_dim_home_type f ON f.home_type_id = b.home_type_key
JOIN r_dim_ownership_type g ON g.ownership_type_id = b.ownership_type_key
WHERE
b.electricity_type_key IN (
8200002,
8200001
)
GROUP BY
d.region_id,
e.year_desc,
home_type_id,
ownership_type_id,
b.electricity_type_key;
Here is another example, using the Oracle data dictionary for input data, for readers who do not have data model to test with:
SELECT owner,
sum(bytes) total_bytes,
100*ratio_to_report(sum(bytes)) over () pct_of_total
FROM dba_segments
GROUP BY owner;

Related

How to subtract from another table in SQL

SELECT
COUNT(ca.Plate) as 'OccupiedElectricSlots'
FROM cities C
JOIN ParkingHouses HS on C.Id = hs.CityId
JOIN ParkingSlots PS on HS.Id = ps.ParkingHouseId
LEFT JOIN Cars Ca on PS.Id = Ca.ParkingSlotsId
WHERE ps.ElectricOutlet = 1
GROUP BY hs.HouseName, C.CityName
SELECT
MAX(Ps.SlotNumber) as 'ParkingSlotTotal'
,MAX(PS.SlotNumber) - Count(ca.Plate) as 'FreeSlots'
,SUM(CAST(PS.ElectricOutlet AS INT)) as 'ElectricOutlet'
,Hs.HouseName
,C.CityName
FROM Cities C
JOIN ParkingHouses HS on C.Id = hs.CityId
JOIN ParkingSlots PS on HS.Id = ps.ParkingHouseId
LEFT JOIN Cars Ca on PS.Id = Ca.ParkingSlotsId
GROUP BY hs.HouseName, C.CityName
How can I subtract the first tables numbers on the second one?
I want to see how many free slots that have electric outlet.
Like this Column ElectricOutlet - OccupiedElectricSlots = result
I'm quite new at SQL, but I have tried to outer apply (don't fully understand it), and I tried to join them both tables togheter. Tried different where conditions but I'm stuck atm.
Your queries are almost identical as far as I can see. You can change your first query to:
SELECT COUNT(CASE WHEN ps.ElectricOutlet = 1 THEN ca.Plate END) as 'OccupiedElectricSlots'
FROM cities C
JOIN ParkingHouses HS on C.Id = hs.CityId
JOIN ParkingSlots PS on HS.Id = ps.ParkingHouseId
LEFT JOIN Cars Ca on PS.Id = Ca.ParkingSlotsId
GROUP BY hs.HouseName, C.CityName
I.e., instead of filtering on ps.ElectricOutlet you just ignore those rows in COUNT. Now you can just:
SELECT
[...]
,SUM(CAST(PS.ElectricOutlet AS INT)) - COUNT(CASE WHEN ...) AS result
[...]
FROM Cities C
JOIN ParkingHouses HS
ON C.Id = hs.CityId
JOIN ParkingSlots PS
ON HS.Id = ps.ParkingHouseId
LEFT JOIN Cars Ca
ON PS.Id = Ca.ParkingSlotsId
GROUP BY hs.HouseName, C.CityName
The MINUS operator is used to subtract the result set obtained by first SELECT query from the result set obtained by second SELECT query.
MINUS compares the data in two tables and returns only the rows of data using the specified columns that exist in the first table but not the second.

SQL - Simple Pivot Table while using Join statements?

I'm trying to transpose my results from the following code which is joining multiple tables together. I know i need to use a PIVOT for this and it may be a simple fix, but i'm having huge difficultly getting the code to work. My code is as follows:
SELECT F.SetValue, D.Name FROM Device D
INNER JOIN Location L ON D.LocationId = L.LocationId
INNER JOIN Fitting F ON L.LocationId = F.LocationId
INNER JOIN LocationTypeFitting LTF ON F.LocationTypeFittingId = LTF.LocationTypeFittingId
WHERE D.DeviceName = 'Device 1' AND LTF.Name LIKE '%Television%';
which prints the following results:
SetValue | Name
===========================
1 | TV_Power
1 | TV_Volume
1 | TV_Source
I need to return the values as below:
TV_Power | TV_Volume | TV_Source
================================
1 | 1 | 1
I know i'll also need a GROUP BY statement, but the the joining of additional tables is making this particular query increasingly difficult. Any help would be very much appreciated.
I would do the following two things:
Wrap the whole query in a sub-query and apply the pivot syntax.
Add another column, such as DeviceName from the Device table (or some other table), so that you can differentiate the rows once the pivot has been executed (I assume there will be more than one row).
it also shows where the group by would go in the comments.
select post_pivot.*
from (
SELECT F.SetValue, D.Name, D.DeviceName FROM Device D
INNER JOIN Location L ON D.LocationId = L.LocationId
INNER JOIN Fitting F ON L.LocationId = F.LocationId
INNER JOIN LocationTypeFitting LTF ON F.LocationTypeFittingId = LTF.LocationTypeFittingId
WHERE D.DeviceName = 'Device 1' AND LTF.Name LIKE '%Television%'
--group by (if needed)
) as pre_pivot
pivot (max(pre_pivot.set_value) for pre_pivot.Name in ([TV_Power], [TV_Volume], [TV_Source])) as post_pivot
Hopefully this will be sufficient or will give you enough to go on.
SELECT 'DeviceType' as DeviceTYpe,* FROM
(
SELECT D.Name, F.SetValue FROM Device D
INNER JOIN Location L ON D.LocationId = L.LocationId
INNER JOIN Fitting F ON L.LocationId = F.LocationId
INNER JOIN LocationTypeFitting LTF ON F.LocationTypeFittingId = LTF.LocationTypeFittingId
WHERE D.DeviceName = 'Device 1' AND LTF.Name LIKE '%Television%'
) AS SourceTable
PIVOT
(
MAX(SetValue)
FOR Name in ([TV_Power], [TV_Volume], [TV_Source])
) As PivotTable

SQL select results not appearing if a value is null

I am building a complex select statement, and when one of my values (pcf_auto_key) is null it will not disipaly any values for that header entry.
select c.company_name, h.prj_number, h.description, s.status_code, h.header_notes, h.cm_udf_001, h.cm_udf_002, h.cm_udf_008, l.classification_code
from project_header h, companies c, project_status s, project_classification l
where exists
(select company_name from companies where h.cmp_auto_key = c.cmp_auto_key)
and exists
(select status_code from project_status s where s.pjs_auto_key = h.pjs_auto_key)
and exists
(select classification_code from project_classification where h.pcf_auto_key = l.pcf_auto_key)
and pjm_auto_key = 11
--and pjt_auto_key = 10
and c.cmp_auto_key = h.cmp_auto_key
and h.pjs_auto_key = s.pjs_auto_key
and l.pcf_auto_key = h.pcf_auto_key
and s.status_type = 'O'
How does my select statement look? Is this an appropriate way of pulling info from other tables?
This is an oracle database, and I am using SQL Developer.
Assuming you want to show all the data that you can find but display the classification as blank when there is no match in that table, you can use a left outer join; which is much clearer with explicit join syntax:
select c.company_name, h.prj_number, h.description, s.status_code, h.header_notes,
h.cm_udf_001, h.cm_udf_002, h.cm_udf_008, l.classification_code
from project_header h
join companies c on c.cmp_auto_key = h.cmp_auto_key
join project_status s on s.pjs_auto_key = h.pjs_auto_key
left join project_classification l on l.pcf_auto_key = h.pcf_auto_key
where pjm_auto_key = 11
and s.status_type = 'O'
I've taken out the exists conditions as they just seem to be replicating the join conditions.
If you might not have matching data in any of the other tables you can make the other inner joins into outer joins in the same way, but be aware that if you outer join to project_status you will need to move the statatus_type check into the join condition as well, or Oracle will convert that back into an inner join.
Read more about the different kinds of joins.

SQL Selecting rows with not the same condition for all

I have to create SQL query that select persons datas. Every person has several grades and I have to select first by time for everyone. I don't know how do it because conditional is different for every person. Below is my current code which doesn't works.
SELECT s.sol_last_name,
g.grade_name,
MIN(sg.sol_grade_date_from)
FROM [dbo].[dim_s####] AS s
LEFT JOIN [dbo].[fact_s####_grade] AS sg ON s.sol_key = sg.sol_grade_sollers_key
LEFT JOIN [dbo].[dim_grade] AS g ON g.grade_key = sg.sol_grade_grade_key
GROUP BY s.sol_last_name,
g.grade_name
HAVING MIN(sg.sol_grade_date_from) = sg.sol_grade_date_from
You can put the earliest date in a subquery, and then inner join there:
SELECT s.sol_last_name,
g.grade_name,
sg.sol_grade_date_from
FROM [dbo].[dim_s####] AS s
INNER JOIN (
select sol_grade_grade_key
,min(sol_grade_date_from) as sol_grade_date_
from from [dbo].[dim_grade]
GROUP BY sol_grade_grade_key) AS g
ON g.grade_key = sg.sol_grade_grade_key
LEFT JOIN [dbo].[fact_s####_grade] AS sg
ON s.sol_key = sg.sol_grade_sollers_key
Use a Common Table Expression (cte) to save some typing. Then do a NOT EXISTS to return a row only if same sol_last_name has no older grade.
WITH CTE (sol_last_name, grade_name, grade_date_from) AS
(
SELECT s.sol_last_name,
g.grade_name,
sg.sol_grade_date_from
FROM [dbo].[dim_s####] AS s
LEFT JOIN [dbo].[fact_s####_grade] AS sg ON s.sol_key = sg.sol_grade_sollers_key
LEFT JOIN [dbo].[dim_grade] AS g ON g.grade_key = sg.sol_grade_grade_key
)
select sol_last_name, grade_name, grade_date_from
from cte as t1
where not exists (select 1 from cte t2
where t2.sol_last_name = t1.sol_last_name
and t2.grade_date_from < t2.grade_date_from)

Using "With" SQL on Oracle 10g causing errors

First ill tell you what the logic in my code is, after my last post it was pointed out to me that my procedure was inefficient and that i should think about logic of my approach..
To put it simply, i want to join a bunch of tables and filter them out to reflect a certain scheme, the process in "Z" in the bellow code
And then parse through that data using y on z...
Looking at the examples online i cant see why this code dosnt work, i have read in a few places that it may be a oracle 10g issue but note sure.. any recommendations would be great
The error i get is "ORA-00904: "Z"."COMMENTS": invalid identifier"
with
z as
(
Select *
FROM
(
iacd_note c
inner join iacd_ncr_note e on C.NOTE_ID=E.NOTE_ID
inner join iacd_ncr f on E.NCR_ID=F.NCR_ID
inner join iacd_ncr_iac g on F.NCR_ID=G.NCR_ID
)
WHERE c.create_date >= date'2014-01-01'
AND c.create_date < date'2014-12-31'
AND G.SCHEME_ID in (36,37,38,25,26,27,28,29,30,31,32,33,34,35,39,40,44,42,43,45, 48,49,50,51,52,55,56,57,58,68,69,70,71)
),
y as
(
Select *
From iacd_asset
)
SELECT y.bridge_no, COUNT(*) AS comment_cnt
FROM y INNER JOIN z
ON REGEXP_LIKE(z.comments, '(^|\W)BN' || y.bridge_no || '(\W|$)', 'i')
GROUP BY y.bridge_no
ORDER BY comment_cnt;
Z.COMMENTS should be part of the merges happening in z
This subquery is invalid SQL syntax:
Select *
FROM
(
iacd_note c
inner join iacd_ncr_note e on C.NOTE_ID=E.NOTE_ID
inner join iacd_ncr f on E.NCR_ID=F.NCR_ID
inner join iacd_ncr_iac g on F.NCR_ID=G.NCR_ID
)
WHERE ...
You can't put parentheses around the FROM clause. Instead:
Select *
FROM iacd_note c
inner join iacd_ncr_note e on C.NOTE_ID=E.NOTE_ID
inner join iacd_ncr f on E.NCR_ID=F.NCR_ID
inner join iacd_ncr_iac g on F.NCR_ID=G.NCR_ID
WHERE ...
Looks like the output of a with clause may not have the same column names as the original tables, so i selected all of z and noticed there was an odd automated name for the row i was after...