SQL> Combine two select statements - sql

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)

Related

Oracle SQL Correlated subquery - Returning count(*) in some columns

I have my initial statement which is :
SELECT TEAM.ID PKEY_SRC_OBJECT,
TEAM.MODF_DAT UPDATE_DATE,
TEAM.MODF_USR UPDATED_BY,
PERSO.FIRST_NAM FISRT_NAME
FROM TEAM
LEFT OUTER JOIN PERSO ON (TEAM.ID=PERSO.TEAM_ID)
I want to calculate some "flags" and return them in my initial statement.
There are 3 flags which can be calculated like this :
1) Flag ISMASTER:
SELECT Count(*)
FROM TEAM_TEAM_REL A, TEAM B
WHERE B.PARTY_PTY_ID = A.RLTD_TEAM_ID
AND CODE = 'Double';
2) Flag ISAGENT:
SELECT Count(*)
FROM TEAM_ROL_REL A, TEAM B
WHERE B.PARTY_PTY_ID = A.TEAM_ID;
3) Flag NUMPACTS:
SELECT Count(*)
FROM TEAM_ROL_REL A,
TEAM_ROL_POL_REL B,
PERSO_POL_STA_REL C,
TEAM D
WHERE A.ROL_CD IN ('1','2')
AND A.T_ROL_REL_ID = B.P_ROL_REL_ID
AND B.P_POL_ID = C.P_POL_ID
AND C.STA_CD = 'A'
AND D.PARTY_PTY_ID = A.TEAM_ID;
To try to achieve this, I've updated my initial statement like this :
WITH ABC AS (
SELECT TEAM.ID PKEY_SRC_OBJECT,
TEAM.MODF_DAT UPDATE_DATE,
TEAM.MODF_USR UPDATED_BY,
PERSO.FIRST_NAM FISRT_NAME
FROM TEAM
LEFT OUTER JOIN PERSO ON (TEAM.ID=PERSO.TEAM_ID)
)
SELECT ABC.*, MAST.ISMASTER, AGENT.ISAGENT, PACTS.NUMPACTS FROM ABC
LEFT OUTER JOIN (
select
RLTD_TEAM_ID,
Count(RLTD_TEAM_ID) OVER (PARTITION BY RLTD_TEAM_ID) as ISMASTER
FROM TEAM_TEAM_REL
WHERE CODE = 'Double'
) MAST
ON ABC.PKEY_SRC_OBJECT = MAST.RLTD_TEAM_ID
LEFT OUTER JOIN (
select
TEAM_ID,
Count(TEAM_ID) OVER (PARTITION BY TEAM_ID) as ISAGENT
FROM TEAM_ROL_REL
) AGENT
ON ABC.PKEY_SRC_OBJECT = AGENT.TEAM_ID
LEFT OUTER JOIN (
select
TEAM_ID,
Count(TEAM_ID) OVER (PARTITION BY TEAM_ID) as NUMPACTS
FROM TEAM_ROL_REL, TEAM_ROL_POL_REL, PERSO_POL_STA_REL
WHERE TEAM_ROL_REL.ROL_CD IN ('1','2')
AND TEAM_ROL_REL.T_ROL_REL_ID = TEAM_ROL_POL_REL.P_ROL_REL_ID
AND TEAM_ROL_POL_REL.P_POL_ID = PERSO_POL_STA_REL.P_POL_ID
AND PERSO_POL_STA_REL.STA_CD = 'A'
) PACTS
ON ABC.PKEY_SRC_OBJECT = PACTS.TEAM_ID;
For the two first flags (ISMASTER and ISAGENT) I get the result in less than 1min, but for the last flag (NUMPACTS) it runs few minutes without provide any result.
I think my statement is too heavy, maybe I should do it in a totally different way.
I think you have perhaps over complicated things.
You could do this (assuming I have understood your requirements correctly) like so:
WITH ttr AS (SELECT rltd_team_id,
COUNT(*) is_master
FROM team_team_rel
AND CODE = 'Double'
GROUP BY rltd_team_id),
trr AS (SELECT team_id,
COUNT(*) is_agent
FROM team_rol_rel
GROUP BY team_id)
pacts AS (SELECT trr1.team_id,
COUNT(*) num_pacts
FROM team_rol_rel trr1
INNER JOIN team_rol_pol_rel trpr ON (trr1.t_rol_rel_id = trpr.p_rol_rel_id)
INNER JOIN perso_pol_sta_rel ppsr ON (trpr.p_pol_id = ppsr.p_pol_id
WHERE trr1.rol_cd IN ('1', '2')
AND ppsr.st_cd = 'A'
GROUP BY trr1.team_id)
SELECT t.id pkey_src_object,
t.modf_dat update_date,
t.modf_usr updated_by,
p.first_nam first_name,
ttr.is_master,
trr.is_agent,
pacts.num_pacts
FROM team t
LEFT OUTER JOIN perso p ON t.id = p.team_id
LEFT OUTER JOIN ttr ON t.party_pty_id = ttr.rltd_team_id
LEFT OUTER JOIN trr ON t.party_pty_id = trr.team_id
LEFT OUTER JOIN pacts ON t.pkey_src_object = pacts.team_id;
N.B. untested, since you didn't provide any test data.

Cannot perform an aggregate function on an expression containing an aggregate or a subquery sql server 2012

I am performing a query and its showing an error Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
the query is
SELECT
tbl_Product.ID,
tbl_Product.ArticleID,
tbl_Product.Title,
tbl_Product.Description,
tbl_Product.Price,
tbl_ProductType.Name,
tbl_Status.StatusName,
tbl_VisibilityStatus.VisibilityStatus,
MAX(
CASE
WHEN tbl_RelatedProduct.TypeOfRelation = 1 THEN 'Bundle'
WHEN tbl_Product.ID IN
(
select tbl_RelatedProduct.Product2ID
from tbl_RelatedProduct
where tbl_RelatedProduct.Product1ID=9 and tbl_RelatedProduct.TypeOfRelation=1) THEN 'Bundle'
END
) 'Bundle',
MAX(
CASE
WHEN tbl_RelatedProduct.TypeOfRelation = 2 THEN 'Follower' END) 'Follower',
MAX(
CASE
WHEN tbl_RelatedProduct.TypeOfRelation = 3 THEN 'Related' END) 'Related'
FROM
tbl_Product inner JOIN
tbl_ProductType ON tbl_Product.ProductTypeId = tbl_ProductType.ID Inner JOIN
tbl_Status ON tbl_Product.StatusID = tbl_Status.ID Inner JOIN
tbl_VisibilityStatus ON tbl_Product.VisibilityID = tbl_VisibilityStatus.ID
left JOIN tbl_RelatedProduct ON tbl_Product.ID = tbl_RelatedProduct.Product1ID
group by
tbl_Product.ID,
tbl_Product.ArticleID,
tbl_Product.Title,
tbl_Product.Description,
tbl_Product.Price,
tbl_ProductType.Name,
tbl_Status.StatusName,
tbl_VisibilityStatus.VisibilityStatus
order by tbl_Product.Title
ANyone know how to help on this...plsss
The below line is causing an issue:
WHEN tbl_Product.ID IN
(
select tbl_RelatedProduct.Product2ID
from tbl_RelatedProduct
where tbl_RelatedProduct.Product1ID=9 and tbl_RelatedProduct.TypeOfRelation=1) THEN 'Bundle'
END
) 'Bundle',
You are using MAX with CASE and one of the statements within CASE uses a subquery, which is causing the error. You might want to consider using joins instead of subqueries to implement this.
SELECT tbl_Product.ID,tbl_Product.ArticleID,tbl_Product.Title,tbl_Product.Description,tbl_Product.Price,tbl_ProductType.Name,tbl_Status.StatusName, tbl_VisibilityStatus.VisibilityStatus,
Case when ((select count(1) from tbl_RelatedProduct where tbl_RelatedProduct.TypeOfRelation = 1 and tbl_RelatedProduct.Product1ID = tbl_Product.Id) > 0) then 1
when ((select count(1) from tbl_RelatedProduct where tbl_RelatedProduct.TypeOfRelation = 1 and tbl_RelatedProduct.Product2ID = tbl_Product.Id) > 0) then 1
else 0 end as Bundle,
(select count(1) from tbl_RelatedProduct where tbl_RelatedProduct.TypeOfRelation = 2 and tbl_RelatedProduct.Product1ID = tbl_Product.Id) as Follower,
(select count(1) from tbl_RelatedProduct where tbl_RelatedProduct.TypeOfRelation = 3 and tbl_RelatedProduct.Product1ID = tbl_Product.Id) as Related
FROM tbl_Product
inner JOIN
tbl_ProductType ON tbl_Product.ProductTypeId = tbl_ProductType.ID Inner JOIN
tbl_Status ON tbl_Product.StatusID = tbl_Status.ID
Inner JOIN
tbl_VisibilityStatus ON tbl_Product.VisibilityID = tbl_VisibilityStatus.ID
Its resolved with this query :)
I think I see what you are trying to do, and it is a bit strange. I would output this all as a single column, and lose the MAX aggregate as follows:
SELECT
p.ID,
p.ArticleID,
p.Title,
p.Description,
p.Price,
pt.Name,
s.StatusName,
v.VisibilityStatus,
CASE
WHEN rp.TypeOfRelation = 1 OR (rp2.Product1ID = 9 AND rp2.TypeOfRelation = 1)
THEN 'Bundle'
WHEN rp.TypeOfRelation = 2
THEN 'Follower'
WHEN rp.TypeOfRelation = 3
THEN 'Related'
ELSE Null
END AS Relation
FROM
tbl_Product p
INNER JOIN tbl_ProductType pt
ON p.ProductTypeId = pt.ID
INNER JOIN tbl_Status s
ON p.StatusID = s.ID
INNER JOIN tbl_VisibilityStatus v
ON p.VisibilityID = v.ID
LEFT JOIN tbl_RelatedProduct rp
ON p.ID = rp.Product1ID
LEFT JOIN tbl_RelatedProduct rp2
ON p.ID = rp2.Product2ID
ORDER BY p.Title
If you would still like to have them in separate columns, just break up the CASE statement as follows:
SELECT
p.ID,
p.ArticleID,
p.Title,
p.Description,
p.Price,
pt.Name,
s.StatusName,
v.VisibilityStatus,
CASE
WHEN rp.TypeOfRelation = 1 OR (rp2.Product1ID = 9 AND rp2.TypeOfRelation = 1)
THEN 'Bundle'
ELSE Null
END AS Bundle,
CASE
WHEN rp.TypeOfRelation = 2
THEN 'Follower'
ELSE Null
END AS Follower,
CASE
WHEN rp.TypeOfRelation = 3
THEN 'Related'
ELSE Null
END AS Related
FROM
tbl_Product p
INNER JOIN tbl_ProductType pt
ON p.ProductTypeId = pt.ID
INNER JOIN tbl_Status s
ON p.StatusID = s.ID
INNER JOIN tbl_VisibilityStatus v
ON p.VisibilityID = v.ID
LEFT JOIN tbl_RelatedProduct rp
ON p.ID = rp.Product1ID
LEFT JOIN tbl_RelatedProduct rp2
ON p.ID = rp2.Product2ID
ORDER BY p.Title

SQL Select Statement SubSelect not working correct

I have 2 SQL Select Queries which I need in two seperare columns. The code I have just now does exactly that, but it also gives 2 nulls. Here is my code :
Select a.Budget, b.Actual_Income FROM
(Select sum(subscriptions.Bill_Amount) as Budget
From imis.dbo.Name Name INNER JOIN imis.dbo.Subscriptions Subscriptions ON Name.ID=Subscriptions.ID
Where Member_Type = 'MM' and Name.Status = 'a' and Product_Code = 'Annual' and Subscriptions.Status = 'a') AS a
FULL JOIN
(SELECT sum(Amount * -1) as Actual_Income
FROM imis.dbo.Name Name INNER JOIN imis.dbo.Trans Trans ON Name.ID=Trans.BT_ID
WHERE PRODUCT_CODE ='ANNUAL' and Transaction_Date >= '21/10/2013' and Batch_Num <> 'DD131031-3') AS b
ON a.Budget = b.Actual_Income
This Is what It returns
Budget Actual_Income
6367005.00 NULL
NULL 665712.37
Any help is much appreciated!
You have a FULL JOIN. Change it to INNER JOIN.
Check this link for better understanding SQL JOINS
If your results were meant to be as the following:
Budget Actual_Income
6367005.00 665712.37
Then you can do subselect or aggregate:
SubSelect:
Select (Select sum(subscriptions.Bill_Amount) as Budget
From imis.dbo.Name Name INNER JOIN imis.dbo.Subscriptions Subscriptions ON Name.ID=Subscriptions.ID
Where Member_Type = 'MM' and Name.Status = 'a' and Product_Code = 'Annual' and Subscriptions.Status = 'a') Budget,
(SELECT sum(Amount * -1) as Actual_Income
FROM imis.dbo.Name Name INNER JOIN imis.dbo.Trans Trans ON Name.ID=Trans.BT_ID
WHERE PRODUCT_CODE ='ANNUAL' and Transaction_Date >= '21/10/2013' and Batch_Num <> 'DD131031-3') as Actual_Income
aggregate:
Select MAX(a.Budget), MAX(b.Actual_Income) FROM
(Select sum(subscriptions.Bill_Amount) as Budget
From imis.dbo.Name Name INNER JOIN imis.dbo.Subscriptions Subscriptions ON Name.ID=Subscriptions.ID
Where Member_Type = 'MM' and Name.Status = 'a' and Product_Code = 'Annual' and Subscriptions.Status = 'a') AS a
FULL JOIN
(SELECT sum(Amount * -1) as Actual_Income
FROM imis.dbo.Name Name INNER JOIN imis.dbo.Trans Trans ON Name.ID=Trans.BT_ID
WHERE PRODUCT_CODE ='ANNUAL' and Transaction_Date >= '21/10/2013' and Batch_Num <> 'DD131031-3') AS b

Aggregation for join ON clause

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')))

Query for logistic regression, multiple where exists

A logistic regression is a composed of a uniquely identifying number, followed by multiple binary variables (always 1 or 0) based on whether or not a person meets certain criteria. Below I have a query that lists several of these binary conditions. With only four such criteria the query takes a little longer to run than what I would think. Is there a more efficient approach than below? Note. tblicd is a large table lookup table with text representations of 15k+ rows. The query makes no real sense, just a proof of concept. I have the proper indexes on my composite keys.
select patient.patientid
,case when exists
(
select c.patientid from tblclaims as c
inner join patient as p on p.patientid=c.patientid
and c.admissiondate = p.admissiondate
and c.dischargedate = p.dischargedate
where patient.patientid = p.patientid
group by c.patientid
having count(*) > 1000
)
then '1' else '0'
end as moreThan1000
,case when exists
(
select c.patientid from tblclaims as c
inner join patient as p on p.patientid=c.patientid
and c.admissiondate = p.admissiondate
and c.dischargedate = p.dischargedate
where patient.patientid = p.patientid
group by c.patientid
having count(*) > 1500
)
then '1' else '0'
end as moreThan1500
,case when exists
(
select distinct picd.patientid from patienticd as picd
inner join patient as p on p.patientid= picd.patientid
and picd.admissiondate = p.admissiondate
and picd.dischargedate = p.dischargedate
inner join tblicd as t on t.icd_id = picd.icd_id
where t.descrip like '%diabetes%' and patient.patientid = picd.patientid
)
then '1' else '0'
end as diabetes
,case when exists
(
select r.patientid, count(*) from patient as r
where r.patientid = patient.patientid
group by r.patientid
having count(*) >1
)
then '1' else '0'
end
from patient
order by moreThan1000 desc
I would start by using subqueries in the from clause:
select q.patientid, moreThan1000, moreThan1500,
(case when d.patientid is not null then 1 else 0 end),
(case when pc.patientid is not null then 1 else 0 end)
from patient p left outer join
(select c.patientid,
(case when count(*) > 1000 then 1 else 0 end) as moreThan1000,
(case when count(*) > 1500 then 1 else 0 end) as moreThan1500
from tblclaims as c inner join
patient as p
on p.patientid=c.patientid and
c.admissiondate = p.admissiondate and
c.dischargedate = p.dischargedate
group by c.patientid
) q
on p.patientid = q.patientid left outer join
(select distinct picd.patientid
from patienticd as picd inner join
patient as p
on p.patientid= picd.patientid and
picd.admissiondate = p.admissiondate and
picd.dischargedate = p.dischargedate inner join
tblicd as t
on t.icd_id = picd.icd_id
where t.descrip like '%diabetes%'
) d
on p.patientid = d.patientid left outer join
(select r.patientid, count(*) as cnt
from patient as r
group by r.patientid
having count(*) >1
) pc
on p.patientid = pc.patientid
order by 2 desc
You can then probably simplify these subqueries more by combining them (for instance "p" and "pc" on the outer query can be combined into one). However, without the correlated subqueries, SQL Server should find it easier to optimize the queries.
Example of left joins as requested...
SELECT
patientid,
ISNULL(CondA.ConditionA,0) as IsConditionA,
ISNULL(CondB.ConditionB,0) as IsConditionB,
....
FROM
patient
LEFT JOIN
(SELECT DISTINCT patientid, 1 as ConditionA from ... where ... ) CondA
ON patient.patientid = CondA.patientID
LEFT JOIN
(SELECT DISTINCT patientid, 1 as ConditionB from ... where ... ) CondB
ON patient.patientid = CondB.patientID
If your Condition queries only return a maximum one row, you can simplify them down to
(SELECT patientid, 1 as ConditionA from ... where ... ) CondA