Aggregation for join ON clause - sql

I have a table item_table like this:
item age
--------------
1 1
1 6
2 2
I have the other table price_table like this:
item pricetype price
--------------------------
1 O 5
1 P 6
1 V 7
2 O 8
2 P 9
2 V 10
So, I want to inner join above two tables.
select *
from item_table i
inner join price_table p
on ...
There are some conditions about the on:
if the average of age of an item is bigger than 3, then I do: inner join price_table on pricetype = 'O' or pricetype = 'P'
If not, then I do: inner join price_table on pricetype = 'O' or pricetype = 'P' or pricetype = 'V'
So there are conditions for on conditions.
I then write the query like this:
select i.item, i.type, p.pricetype, p.price
from item_table i
inner join price_table p on i.item = p.item
and (avg(i.age) >= 3 and p.pricetype in ('O', 'P'))
or (avg(i.age) < 3 and p.pricetype in ('O', 'P', 'V'))
The error is given: An aggregate cannot appear in an ON clause unless it is in a subquery contained in a HAVING clause or select list, and the column being aggregated is an outer reference.
I can't move the avg to Having because other conditions are depending on the avg.
How can I write the select query?

select *
from (
select item, avg(age) as AvgAge
from item_table
group by item
) ia
inner join price_table p on ia.item = p.item
and ((ia.AvgAge >= 3 and p.pricetype in ('O', 'P'))
or (ia.AvgAge < 3 and p.pricetype in ('O', 'P', 'V')))
SQL Fiddle Example 1
This can be simplified to:
select *
from (
select item, avg(age) as AvgAge
from item_table
group by item
) ia
inner join price_table p on ia.item = p.item
and (p.pricetype in ('O', 'P')
or (ia.AvgAge < 3 and p.pricetype = 'V'))
SQL Fiddle Example 2

Did you try placing the aggregation in a subquery, then you have the avg() value for use in the JOIN clause:
select i.item, i.type, p.pricetype, p.price
from
(
select avg(i.age) age, i.item, i.type -- not sure where type is coming from in your OP as it is not in the table you showed
from item_table i
group by i.item, i.type
) i
inner join price_table p
on i.item = p.item
and ((i.age>= 3 and p.pricetype in ('O', 'P'))
or (i.age < 3 and p.pricetype in ('O', 'P', 'V')))

Related

SQL : Percentage Completed

I need to have a SQL query to calculate the percentage of courses completed by location which are different SQL tables.
Courses table has a Status = 'C' (Completed status).
select Locations.Name, ( ??? ) as PercentCompleted
from Locations inner join Candidates ON Locations.Id = Candidates.SpecifiedLocation
inner join Courses on Candidates.Id = Courses.CandidateId
Group By Locations.Name
I want the results to be:
Location PercentCompleted
Loc1 10
Loc2 50
Loc3 75
where 10, 50 and 75 are percentages of courses completed per location.
Can this be achieved with a single SQL query?
If I understand correctly, I think you can do:
select l.Name,
avg(case when co.status = 'C' then 100.0 else 0 end) as PercentCompleted
from Locations l inner join
Candidates c
on l.Id = c.SpecifiedLocation inner join
Courses co
on c.Id = co.CandidateId
group by l.name;
try like below
select Locations.Name, (sum(case when Status = 'C' then 1 else 0 end)/(select count(*)
from Candidates c where c.SpecifiedLocation=Locations.Id))*100
as PercentCompleted
from Locations inner join Candidates ON Locations.Id = Candidates.SpecifiedLocation
inner join Courses on Candidates.Id = Courses.CandidateId
Group By Locations.Name

SQL count in select query

I have a sql query that creates a label in a cab file for a shopping company. I want to include the amount of packages in an order, some have multiple.
Each returns line in my select query has an I’d and contains a package but I need to count them.
So I have:
Name Email Weight Price ID
Joe B J#.com 10 12.5. 1
Joe B J#.com 10 12.5. 1
Joe C JC#.com 10 14.5. 2
How can I count the ID’s to return a column called pieces and in this example it would be 2 for ID 1 and 1 for ID 2
Thanks
James
enter code here
Select
'WPX' As 'Product Code',
delivery_header.dh_datetime As 'Shipment Date',
'G' As 'Shipment Type',
order_header_detail.ohd_delivery_email As 'Receiver Email Address',
variant_detail.vad_weight As 'Shipment Weight in KG',
(lots of other fields....)
delivery_header.dh_number As 'Shippers Reference',
(SELECT Count(*)
FROM delivery_header
WHERE
dh_number = OU.dh_number
) As 'Number of Pieces',
From delivery_line_item Inner Join
delivery_header On delivery_header.dh_id = delivery_line_item.dli_dh_id
Inner Join
order_line_item On delivery_line_item.dli_oli_id = order_line_item.oli_id
Inner Join
variant_detail On variant_detail.vad_id = order_line_item.oli_vad_id
Inner Join
order_header On order_header.oh_id = order_line_item.oli_oh_id Inner Join
stock_location On stock_location.sl_id = order_line_item.oli_sl_id Inner Join
customer_detail On customer_detail.cd_id = order_header.oh_cd_id Inner Join
order_header_detail On order_header.oh_id = order_header_detail.ohd_oh_id
Left Join
order_header_analysis On order_header.oh_id = order_header_analysis.oha_oh_id
Left Join
order_customer_analysis On order_header.oh_id =
order_customer_analysis.oca_oh_id Left Join
order_delivery_analysis On order_header.oh_id =
order_delivery_analysis.oda_oh_id Left Join
order_line_analysis On order_line_item.oli_id = order_line_analysis.ola_oli_id
Left Join
order_line_product_analysis On order_line_item.oli_id =
order_line_product_analysis.olpa_oli_id Left Join
order_line_variant_analysis On order_line_item.oli_id =
order_line_variant_analysis.olva_oli_id Inner Join
product_detail On product_detail.pd_id = variant_detail.vad_pd_id Inner Join
delivery_method On delivery_method.dm_id = order_header_detail.ohd_dm_id
Inner Join
delivery_method [Delivery Method] On [Delivery Method].dm_id =
order_header_detail.ohd_dm_id Inner Join
currency On currency.c_id = order_header.oh_c_id And currency.c_id =
delivery_method.dm_c_id And currency.c_id = [Delivery Method].dm_c_id
Where
delivery_header.dh_number IN (199364,199363,199362,199360)
Order By delivery_header.dh_number
You can use GROUP BY with COUNT like this:
SELECT ID, COUNT(*) as count FROM tbl GROUP BY ID
What I understood from your question is that, you are looking for output like
Name Email Weight Price ID Pieces
Joe B J#.com 10 12.5. 1 2
Joe B J#.com 10 12.5. 1 2
Joe C JC#.com 10 14.5. 2 1
Using following query you should get the desired output in most of the DBMS.
SELECT NAME,
EMAIL,
WEIGHT,
PRICE,
ID,
(SELECT Count(*)
FROM <YOUR_TABLE_NAME>
WHERE ID= OU.ID) AS Pieces
FROM <YOUR_TABLE_NAME> OU
Similar Query for MS SQL Server can be
SELECT NAME, EMAIL,WEIGHT , Price, ID,CT.Pieces
FROM <YOUR_TABLE_NAME> OU
CROSS APPLY
(
SELECT COUNT(*) AS Pieces FROM <YOUR_TABLE_NAME> IU WHERE OU.ID=IU.ID
)CT

Add column that counts times value occurs in results

I have a query with 5 columns. I want to add 1 column that defines the number of times the value in column 2 (ItemCode) occurs in the results.
This is my query:
SELECT
Items.Description_0 AS [Items.Description],
Items.ItemCode,
warehouse_location,
Stock.Quantity AS StockQty,
Stock.warehouse
FROM
((SELECT gbkmut.artcode, gbkmut.warehouse, ISNULL(gbkmut.warehouse_location,'') AS warehouse_location,
SUM(gbkmut.aantal) AS Quantity FROM gbkmut
INNER JOIN Items ON Items.ItemCode = gbkmut.artcode
INNER JOIN voorrd ON voorrd.artcode=gbkmut.artcode
AND voorrd.magcode=gbkmut.warehouse
INNER JOIN ItemUnits ON ItemUnits.Unit = Items.PackageDescription
WHERE gbkmut.reknr = Items.GLAccountDistribution
AND (( gbkmut.transtype IN ('N', 'C', 'P', 'X')
AND gbkmut.datum BETWEEN {d '2000-01-09'} AND {d '2031-02-08'} ) )
AND (gbkmut.warehouse='MAG1' )
AND Items.Type IN ('S','B')
AND NOT (Items.GLAccountAsset IS NOT NULL AND Items.IsSerialNumberItem=1) AND NOT (Items.Type = 'S' AND ItemUnits.UnitType = 'T')
GROUP BY gbkmut.artcode, gbkmut.warehouse, ISNULL(gbkmut.warehouse_location,'')
HAVING SUM(gbkmut.aantal) > 0)) Stock
INNER JOIN Items ON Items.ItemCode=Stock.artcode
WHERE Items.ItemCode like '10.27021%'
ORDER BY Items.ItemCode
Lazy version, use a common table expression (cte):
with cte as
(
[your huge select]
)
select t1.*, t2.codecount
from cte t1
join (select ItemCode, count(*) as codecount from cte group by ItemCode) as t2
ON t2.ItemCode = t1.ItemCode

Pulling distinct values for individuals

I am trying to get a read out of all patients with a result in any of the 4 axis categories on their most recent date from proc_chron and where the patient is a(active) under their case_status well excluding patients where all four categories are null. SQL server 2005.
select
pct.patient_id,
pct.clinic_id,
pct.axis_I_II_1,
pct.axis_I_II_2,
pct.axis_I_II_3,
pct.axis_III_1,
pct.proc_chron
from patient_clin_tran pct
join patient p
on p.patient_id = pct.patient_id
group by pct.patient_id, pct.clinic_id, pct.axis_I_II_1,pct.axis_I_II_2, pct.axis_I_II_3, pct.axis_III_1, p.case_status, pct.proc_chron
having p.case_status = 'a' and pct.proc_chron = (select max(pct.proc_chron))
order by pct.patient_id
select
pct.patient_id,
pct.clinic_id,
pct.axis_I_II_1,
pct.axis_I_II_2,
pct.axis_I_II_3,
pct.axis_III_1,
pct.proc_chron
from patient_clin_tran pct
join patient p
on p.patient_id = pct.patient_id
where p.case_status = 'a'
and pct.proc_chron = (select max(proc_chron) from patient_clin_tran pct2 where pct2.patientid = p.patientid)
and (pct.axis_I_II_1 is not null or pct.axis_I_II_2 is not null or pct.axis_I_II_3 is not null or pct.axis_I_II_4 is not null)
order by pct.patient_id

SQL> Combine two select statements

1
SELECT v.NAME,
CASE
WHEN va.state_territory_province = 'Illinois'
THEN 'Illinois'
END Vendors
FROM grocery.vendor v
INNER JOIN grocery.vendor_address va ON va.vendor_id = v.vendor_id
WHERE va.state_territory_province = 'Illinois';
2
SELECT vendor_id,
CASE
WHEN count(DISTINCT product_id) > 2
THEN 'High'
END
FROM grocery.can_supply
HAVING count(DISTINCT product_id) > 2
GROUP BY vendor_id
ORDER BY vendor_id;
The question I have to try and answer is:
What are the full names of all vendors who can supply more than one item or are based in Illinois?
I know how to write them separately, but I need to write them together and it is puzzling me.
EDITED
I tried to UNION and it gave me this error:
*
ERROR at line 1:
ORA-00923: FROM keyword not found where expected
SQL> SELECT vc.contact_name,
CASE
WHEN va.state_territory_province = 'Illinois'
THEN 'Availible'
END CheckAvail
FROM grocery.vendor_address va
INNER JOIN grocery.vendor_contact vc ON vc.vendor_id = va.vendor_id
WHERE va.state_territory_province = 'Illinois' union select vc.contact_name
CASE
WHEN count(distinct cs.product_id) > 1
THEN 'Avail'
END CheckAvail from grocery.vendor cs;
If you have two queries that return the same fields but have different selection logic, you combine their results using the UNION operator, e.g.
select c1, c2 from ... where ...
union
select c1, c2 from ... where ...
I would probably use a sub-select like this
SELECT v.NAME,
CASE
WHEN va.state_territory_province = 'Illinois'
THEN 'Illinois'
END Vendors
FROM grocery.vendor v
INNER JOIN grocery.vendor_address va ON va.vendor_id = v.vendor_id
WHERE va.state_territory_province = 'Illinois' AND v.vendor_id IN (
SELECT vendor_id FROM grocery.can_supply
HAVING count(DISTINCT product_id) > 2
GROUP BY vendor_id
ORDER BY vendor_id);
There's a number of options here. You can do left joins (see below) exists statements, unions. CTP style pre-selects.
SELECT v.NAME
, ISNULL(va.state_territory_province, '') Vendors
, ISNULL(inv.Desc, '') AS Desc
FROM grocery.vendor v
LEFT JOIN grocery.vendor_address va ON va.vendor_id = v.vendor_id AND va.state_territory_province = 'Illinois'
LEFT JOIN (SELECT vendor_id,
CASE WHEN count(DISTINCT product_id) > 2 THEN 'High' END AS desc
FROM grocery.can_supply
HAVING count(DISTINCT product_id) > 2
GROUP BY vendor_id) inv ON inv.Vendor_Id = v.Vendor_Id
WHERE 1=1
AND (va.Vendor_Id NOT IS NULL OR inv.Vendor_ID NOT IS NULL)