How to subtract from another table in SQL - 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.

Related

Pulling cost field from a sub-query

I wrote the following query to pull a unit cost from another table COSreport into my profitability query ProfitabilityReport and am having a problem with my sub-query.
select
i.tranid
, it.item_id
, it.displayname
, tl.Item_Count * -1 Unit_Qty
, case when tl.Item_Count=0 then 0
else ((tl.GROSS_AMOUNT * -1)/ Item_Count) * -1
end as PricePerUnit,
**(select sum(c.tranamt) from ns.COSreport c
inner join ns.ProfitabilityReport d
on c.InvoiceID = d.tranid
and c.item_id = d.item_id) as 'True Cost'**
, '0' 'Cost Per M'
from ns.tinvoice i
join ns.transaction_lines tl on i.transaction_id = tl.transaction_id
join ns.Customers cust on c.customer_id = i.ENTITY_ID
join ns.items it on it.item_id = tl.item_id
left join ns.ITEM_CLASSIFICATION it_class on it_class.list_id =
it.ITEM_CLASSIFICATION_ID
where list_item_name IS NOT NULL
and i.tranid = '1262INV'
I'm joining on both the invoice id and item id so that the proper cost is pulled across for the given invoice and item from COSReport.
However, the true cost is not coming up with the unit cost but instead is summing up the cost field for the entire table.
See below for example using invoice # 1262INV specified in the query above. The cost should be 1.04, .26, and 4 respectively vs 138m.
Any help getting this cleared up would be appreciated
I actually prefer using CTEs for readability. You can take your subquery, put it into a CTE, and then join it in your main query, but you'll want to add the tranid and item_id fields to the CTE so you can use them in your join.
EDIT: since you're using Azure SQL Server, you don't need the semicolon before the WITH.
WITH TrueCosts AS
(
SELECT
d.tranid
,d.item_id
,TrueCost = SUM(c.tranamt)
FROM ns.COSreport c
INNER JOIN ns.ProfitabilityReport d
ON c.InvoiceID = d.tranid
AND c.item_id = d.item_id
GROUP BY d.tranid
,d.item_id
)
SELECT
i.tranid
, it.item_id
, it.displayname
, tl.Item_Count * -1 Unit_Qty
, case when tl.Item_Count=0 then 0
else ((tl.GROSS_AMOUNT * -1)/ Item_Count) * -1
END as PricePerUnit
, tc.TrueCost AS 'True Cost'
, '0' AS 'Cost Per M'
FROM ns.tinvoice i
JOIN ns.transaction_lines tl on i.transaction_id = tl.transaction_id
JOIN ns.Customers c on c.customer_id = i.ENTITY_ID
JOIN ns.items it on it.item_id = tl.item_id
LEFT JOIN ns.ITEM_CLASSIFICATION it_class on it_class.list_id = it.ITEM_CLASSIFICATION_ID
LEFT JOIN TrueCosts tc ON tc.tranid = i.tranid AND tc.item_id = tl.item_id
WHERE list_item_name IS NOT NULL
AND i.tranid = '1262INV'
You have a couple of problems.
In your subquery you use the alias c for COSReport. You also use the alias c for customers in your outer query.
The bigger problem is that your subquery isn't correlated to your outer query at all. That's why it's summing up the entire table.
To correlate your subquery, you need to join (in the subquery) to one of the tables in the outer query. Not sure of your tables or data, but at a guess, I'd say you want to use ns.tinvoice i in a WHERE clause in your subquery.

sql inner join over multiple tables

I've given this relational scheme and following task:
Inner Join: Return a list of professors, which gives
'lehrveranstaltung' of the 'fachbereich' with the name 'informatik'.
* print 'vorname', 'ho_name', 'lv_name'
* output should sort surnames in ascending order and if they're the same in descending order
* identical lines should online shown once
now I came up with following query:
select distinct
v.vorname,
h.ho_name,
l.lv_name
--print wanted, only once
from
vorname v,
hochschulangehoeriger h,
lehrveranstaltung l
-- from these tables
inner join fachbereich f on f.fb_name = 'Informatik'
-- only the 'informatik' events
inner join prof_haelt_lv on l.lv_nr = pl.lv_nr
-- make sure 'lehrveranstaltung' is from a professor
inner join mitarbeiter mit on pl.pers_Nr = mit.pers_Nr
-- make sure dude is a prof
where
mit.ho_nr = h.ho_nr
and
mit.ho_nr = v.ho_nr -- give only names from prof
order by
2 asc,
3 desc; -- order rules
I think this works for me (can't test it properly). But when I look at it I'll wish that I came up for a bether solution since this looks kinda ugly and wrong for me.
Is there a bether way of doing this? (Have to use inner join)
Based on the table you have, you may use the following SQL statement
SELECT DISTINCT v.vorname,
h.ho_name,
l.lv_name
FROM vorname v
INNER JOIN hochschulangehoeriger h
ON v.ho_nr = h.ho_nr
INNER JOIN mitarbeiter m
ON m.ho_nr = h.ho_nr
INNER JOIN fachbereich f
ON f.fb_nr = m.fb_nr
AND f.fb_name = 'Informatik'
INNER JOIN lehrveranstaltung l
ON l.fb_nr = f.nb_nr
INNER JOIN professor p
ON p.pers_nr = m.pers_nr
INNER JOIN prof_haelt_lv pl
ON pl.pers_nr = p.pers_nr
AND pl.lv_nr = l.lv_nr
ORDER BY 2,
3 DESC;
Also, these section on your SQL, this has no connection to any table in your SQL
inner join fachbereich f on f.fb_name = 'Informatik'
-- only the 'informatik' events
you forgot the alias for prof_haelt_lv
inner join prof_haelt_lv on l.lv_nr = pl.lv_nr
-- make sure 'lehrveranstaltung' is from a professor

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-Get and replace minimum value of a query's field

I have the following query in Postgresql :
SELECT mq.nombre,sa.nombre,COUNT(DISTINCT(ae.numero_serie)),SUM(im.fin-im.inicio),MIN(pz.fecha_inicio)
FROM item_metraje AS im LEFT JOIN articulo_especificado AS ae ON (im.id_articulo_especificado = ae.id)
LEFT JOIN articulo AS a ON (ae.id_articulo = a.id)
LEFT JOIN serie_articulo AS sa ON (a.id_serie_articulo = sa.id)
LEFT JOIN reporte_perforacion AS rp ON (rp.id = im.id_reporte_perforacion)
LEFT JOIN pozo AS pz ON (pz.id=rp.id_pozo) LEFT JOIN proyecto AS p ON (p.id=pz.id_proyecto)
LEFT JOIN maquina_perforacion AS mq ON (mq.id = pz.id_maquina)
WHERE p.id = 2 GROUP BY mq.nombre,sa.nombre
and the result is :
However I want to put the minimum date for the rows that have the same value of the field 'nombre', for example for the value 'JM04' the three rows must have the date 2015-01-25 because it is the minimum value of the three rows.
Excuse me for my English and thanks for all.
You can use Window functions for this purpose. MIN(pz.fecha_inicio) over (partition by mq.nombre).
Therefore the final query is,
SELECT z.nombre1,z.nombre2,z.count,z.sum ,MIN(z.date) over (partition by z.nombre1) from
(SELECT mq.nombre as nombre1 ,sa.nombre as nombre2,COUNT(DISTINCT(ae.numero_serie)) as count,SUM(im.fin-im.inicio) as sum ,pz.fecha_inicio as date
FROM item_metraje AS im LEFT JOIN articulo_especificado AS ae ON (im.id_articulo_especificado = ae.id)
LEFT JOIN articulo AS a ON (ae.id_articulo = a.id)
LEFT JOIN serie_articulo AS sa ON (a.id_serie_articulo = sa.id)
LEFT JOIN reporte_perforacion AS rp ON (rp.id = im.id_reporte_perforacion)
LEFT JOIN pozo AS pz ON (pz.id=rp.id_pozo) LEFT JOIN proyecto AS p ON (p.id=pz.id_proyecto)
LEFT JOIN maquina_perforacion AS mq ON (mq.id = pz.id_maquina)
WHERE p.id = 2 GROUP BY mq.nombre,sa.nombre)z
You can modify this with the help of order by or having clauses inside window function as you want. I tried this with my own data set. Hope this helps.

Reduce Runtime of T-SQL Query

-- WITH POD was causing the issue, removing this code reduced 2 year pull to 3 mins.
Will post new question to figure out best way to include POD data.
--Edit for clarity, I am a read only user to these tables.
I wrote the below query, but it takes a very long time to execute (20min).
It is currently limited to 1 month, but user wants at least 1 year preferably 2. I assume this would scale time to hours.
Can anyone take a look at let me know if there is a BKM I am not using to improve performance?
Or if there is a better method for a report of this size? At 2 years, it would return ~100K rows from 17 tables.
WITH
POD AS
(
SELECT SHIPMENTS.Delivery
,SHIPMENTS.Shipment_Number
,PROOF_OF_DELIVERY.Shipping_Carrier
,PROOF_OF_DELIVERY.Tracking_Number
,PROOF_OF_DELIVERY.Ship_Method
,PROOF_OF_DELIVERY.POD_Signature
,PROOF_OF_DELIVERY.POD_Date
,PROOF_OF_DELIVERY.POD_Time
FROM
SHIPMENTS
LEFT JOIN PROOF_OF_DELIVERY
ON SHIPMENTS.Shipment_Number = PROOF_OF_DELIVERY.Delivery_Or_Shipment
WHERE Load_Date IN
(
SELECT MAX(Load_Date)
FROM PROOF_OF_DELIVERY
GROUP BY Delivery_Or_Shipment
)
)
SELECT DISTINCT GI.GOODS_ISSUE_DOCUMENT_ID
,GI.SALES_ORDER_ID
,GI.SALES_ORDER_LINE_ID
,GI.SALES_ORDER_TYPE_CODE
,GI.DELIVERY_HEADER_ID
,GI.DELIVERY_ITEM_ID
,FD.FISCAL_MONTH_CODE
,GI.MATERIAL_NUMBER
,GI.SHIPPED_QTY
,SO.ORDERER_NAME
,SO.CREATED_BY
,SO.CONTACT_PERSON
,GI.SOLD_TO_CUSTOMER_ID
,GI.SHIP_TO_CUSTOMER_ID
,GI.ORIGINAL_COMMIT_DATE
,GI.SHIP_FROM_PLANT_ID
,GI.ACTUAL_PGI_DATE
,GI.CUSTOMER_PO_NUMBER
,GI.SHIPPED_PRICE
,(GI.SHIPPED_PRICE * GI.SHIPPED_QTY) AS EXT_SHIPPED_PRICE
,GI.SALES_ORGANIZATION_CODE
,GI.DELIVERY_NOTE_PRIORITY_CODE
,FD.FISCAL_WEEK_CODE
,DV.DIVISION_CODE
,DN.Delivery_Item_Creation_Date
,SOLD.CUSTOMER_SHORT_NAME AS SOLD_TO_CUSTOMER_SHORT_NAME
,SHIP.CUSTOMER_SHORT_NAME AS SHIP_TO_CUSTOMER_SHORT_NAME
,SHIP.Customer_Site_Name
,SHIP.REGION_NAME
,MATD.MATERIAL_DESCRIPTION
,MATD.STANDARD_COST
,(MATD.STANDARD_COST * GI.SHIPPED_QTY) AS EXT_STANDARD_COST
,MATD.GLOBAL_EVENT
,PLT.LEAD_TIME_FOR_ORIGINAL_COMMIT
,OPRM.BASE_PART_CODE
,MATD.PRODUCT_INSP_MEMO
,MATD.MATERIAL_PRICING_GROUP_CODE
,MATD.MATERIAL_STATUS AS MMPP
,PIM.PIM_PBG_GROUPING
,SOL.SHIPPING_CONDITION
,SVO.SERVICE_ORDER_NUM
,SO.CREATION_TIME AS SO_CREATION_TIME
,SOL.CREATED_TIME AS SO_LINE_CREATED_TIME
,SOL.SHIPPING_POINT
,SDT.SALES_DOCUMENT_TYPE_CODE AS SVO_DOCUMENT_TYPE_CODE
,EQU.EQUIPMENT_NUM
,EQU.SERIAL_NUMBER
,EQU.CUSTOMERTOOLID
,POD.Shipment_Number
,POD.Shipping_Carrier
,POD.Tracking_Number
,POD.Ship_Method
,POD.POD_Signature
,POD.POD_Date
,POD.POD_Time
,DATEDIFF(dd,SO.CREATION_TIME,GI.ACTUAL_PGI_DATE) AS Cycle_Time_to_PGI_Days
,DATEDIFF(hh,SO.CREATION_TIME,GI.ACTUAL_PGI_DATE) AS Cycle_Time_to_PGI_Hours
FROM GOODS_ISSUE AS GI
INNER JOIN dbo.Delivery_Notes AS DN
ON GI.DELIVERY_HEADER_ID = DN.DELIVERY_HEADER_CODE AND GI.DELIVERY_ITEM_ID = DN.DELIVERY_ITEM_CODE
INNER JOIN dbo.Customer_View AS SOLD
ON GI.SOLD_TO_CUSTOMER_ID = SOLD.CUSTOMER_CODE
INNER JOIN dbo.Customer_View AS SHIP
ON GI.SOLD_TO_CUSTOMER_ID = SHIP.CUSTOMER_CODE
INNER JOIN dbo.MATERIAL_DETAILS AS MATD
ON GI.MATERIAL_NUMBER = MATD.MATERIAL_NUMBER
INNER JOIN dbo.OPR_MATERIAL_DIM AS OPRM
ON OPRM.MATERIAL_NUMBER = GI.MATERIAL_NUMBER
LEFT JOIN dbo.SM_DATE_DIM AS FD
ON CAST(FD.CALENDAR_DAY AS DATE) = CAST(GI.ACTUAL_PGI_DATE AS DATE)
LEFT JOIN dbo.DIM_PUBLISHED_LEAD_TIME_COMMIT AS PLT
ON PLT.MATERIAL_NUMBER = OPRM.BASE_PART_CODE
LEFT JOIN dbo.PRODUCT_INSP_MEMO_DIM AS PIM
ON PIM.PRODUCT_INSP_MEMO = MATD.PRODUCT_INSP_MEMO
INNER JOIN dbo.SM_SALES_ORDER_LINE_FACT AS SOL
ON SOL.SALES_ORDER_CODE = GI.SALES_ORDER_ID AND SOL.SALES_ORDER_LINE_CODE = GI.SALES_ORDER_LINE_ID
INNER JOIN dbo.SM_SALES_ORDER_FACT AS SO
ON SO.SALES_ORDER_CODE = GI.SALES_ORDER_ID
INNER JOIN dbo.SM_DIVISION_DIM AS DV
ON SO.DIVISION_SID = DV.DIVISION_SID
LEFT JOIN dbo.SERVICE_ORDER_FACT AS SVO
ON SVO.SERVICE_ORDER_NUM = SO.SERVICE_ORDER_NUMBER
LEFT JOIN dbo.SM_SALES_DOCUMENT_TYPE_DIM AS SDT
ON SDT.SALES_DOCUMENT_TYPE_SID = SVO.SALES_DOCUMENT_TYPE_SID
LEFT JOIN dbo.SM_EQUIPMENT_DIM AS EQU
ON EQU.EQUIPMENT_SID = SVO.EQUIPMENT_SID
LEFT JOIN POD
ON POD.Delivery = GI.DELIVERY_HEADER_ID
WHERE GI.ACTUAL_PGI_DATE > GETDATE()-32
AND SOLD_TO_CUSTOMER_ID IN (0010000252,0010000898,0010001121,0010001409,0010001842,0010001852,0010001879,0010001977,0010001978,0010002021,0010002202,0010002227,0010002982,0010003118,0010003176,0010003294,0010005492,0010006904,0010007048,0010007080,0010010381,0010010572,0010010905,0010011999,0010012014,0010012048,0010012571,0010013124,0010013711,0010013713,0010013824,0010014180,0010014188,0010014333,0010015059,0010015313,0010015414,0010015541,0010015544,0010015550)
A CTE is just syntax
I suspect that CTE is evaluated many times
Materialze the CTE to #temp with indexe(s) so it is run once
This cast will hurt it
Make those columns true dates and index them
ON CAST(FD.CALENDAR_DAY AS DATE) = CAST(GI.ACTUAL_PGI_DATE AS DATE)
That where negates the left so you can just do a join
Also that MAX(Load_Date) could match on another shipment
SELECT SHIPMENTS.Delivery
,SHIPMENTS.Shipment_Number
,PROOF_OF_DELIVERY.Shipping_Carrier
,PROOF_OF_DELIVERY.Tracking_Number
,PROOF_OF_DELIVERY.Ship_Method
,PROOF_OF_DELIVERY.POD_Signature
,PROOF_OF_DELIVERY.POD_Date
,PROOF_OF_DELIVERY.POD_Time
FROM SHIPMENTS
JOIN PROOF_OF_DELIVERY
ON SHIPMENTS.Shipment_Number = PROOF_OF_DELIVERY.Delivery_Or_Shipment
WHERE PROOF_OF_DELIVERY.Load_Date IN
(
SELECT MAX(Load_Date)
FROM PROOF_OF_DELIVERY
GROUP BY Delivery_Or_Shipment
)
Pull this up into the join
INNER JOIN dbo.Customer_View AS SOLD
ON GI.SOLD_TO_CUSTOMER_ID = SOLD.CUSTOMER_CODE
AND GI.SOLD_TO_CUSTOMER_ID IN (0010000252,0010000898,0010001121,0010001409,0010001842,0010001852,0010001879,0010001977,0010001978,0010002021,0010002202,0010002227,0010002982,0010003118,0010003176,0010003294,0010005492,0010006904,0010007048,0010007080,0010010381,0010010572,0010010905,0010011999,0010012014,0010012048,0010012571,0010013124,0010013711,0010013713,0010013824,0010014180,0010014188,0010014333,0010015059,0010015313,0010015414,0010015541,0010015544,0010015550)