How to count the times a particular item appears in a query output - sql

I have a query with a lot of tables and joins. So a simple Count(column) and Group By isn't working correctly.
The query returns an item list. I want to calculate how many times each item appears in that list.
Here's my query:
SELECT DECODE(BOM.ORGANIZATION_ID,203, 'CEC', 328, '3PL', 204, 'SIM') ORGANIZATION_CODE,
BOM.ORGANIZATION_ID,
MSI.SEGMENT1 "PARENT_ITEM",
MSIC.SEGMENT1 "COMPONENT_ITEM",
SUM(NVL(MMT.TRANSACTION_QUANTITY,0)) * NVL(SUM(CIC.ITEM_COST),0) "ANNUAL_MONEY",
SUM(NVL(MMT.TRANSACTION_QUANTITY,0)) "ANNUAL_QTY_USG",
NVL(SUM(CIC.ITEM_COST),0) "AVG_COST",
SUM(NVL(MSI.POSTPROCESSING_LEAD_TIME,0) + NVL(MSI.PREPROCESSING_LEAD_TIME,0)) LEAD_TIME
FROM BOM_BILL_OF_MATERIALS BOM
LEFT JOIN MTL_SYSTEM_ITEMS MSI
ON MSI.ORGANIZATION_ID = BOM.ORGANIZATION_ID
AND MSI.INVENTORY_ITEM_ID = BOM.ASSEMBLY_ITEM_ID
AND MSI.ENABLED_FLAG = 'Y'
AND MSI.BOM_ENABLED_FLAG = 'Y'
LEFT JOIN CST_ITEM_COSTS CIC
ON CIC.ORGANIZATION_ID = BOM.ORGANIZATION_ID
AND CIC.INVENTORY_ITEM_ID = BOM.ASSEMBLY_ITEM_ID
AND CIC.COST_TYPE_ID = 1
LEFT JOIN MTL_MATERIAL_TRANSACTIONS MMT
ON MMT.INVENTORY_ITEM_ID = BOM.ASSEMBLY_ITEM_ID
AND MMT.ORGANIZATION_ID = BOM.ORGANIZATION_ID
AND MMT.TRANSACTION_TYPE_ID IN (33,34,17)
LEFT JOIN MTL_TRANSACTION_TYPES MTT
ON MTT.TRANSACTION_TYPE_ID = MTT.TRANSACTION_TYPE_ID
LEFT JOIN BOM_INVENTORY_COMPONENTS BIC
ON BIC.BILL_SEQUENCE_ID = BOM.COMMON_BILL_SEQUENCE_ID
AND NVL(bic.disable_date, sysdate+1) > sysdate
LEFT JOIN BOM_COMPONENTS_B BCB
ON BIC.COMPONENT_SEQUENCE_ID = BCB.COMPONENT_SEQUENCE_ID
LEFT JOIN BOM_STRUCTURES_B BSB
ON BCB.BILL_SEQUENCE_ID = BSB.BILL_SEQUENCE_ID
LEFT JOIN MFG_LOOKUPS ML
ON ML.LOOKUP_CODE = BIC.WIP_SUPPLY_TYPE
AND ML.LOOKUP_CODE = BIC.ENFORCE_INT_REQUIREMENTS
AND ML.LOOKUP_TYPE = 'MTL_EAM_ITEM_TYPE'
LEFT JOIN MTL_SYSTEM_ITEMS MSIC
ON MSIC.ORGANIZATION_ID = BOM.ORGANIZATION_ID
AND MSIC.INVENTORY_ITEM_ID = BIC.COMPONENT_ITEM_ID
LEFT JOIN BOM_OPERATIONAL_ROUTINGS BOR
ON BOR.ASSEMBLY_ITEM_ID = BOM.ASSEMBLY_ITEM_ID
AND BOR.ORGANIZATION_ID = BOM.ORGANIZATION_ID
WHERE BOM.ORGANIZATION_ID = NVL(:P_ORG_ID,BOM.ORGANIZATION_ID)
AND EXISTS
(SELECT 'X'
FROM MTL_MATERIAL_TRANSACTIONS MMT
WHERE MMT.TRANSACTION_DATE >= ADD_MONTHS (SYSDATE,-12)
AND MSI.INVENTORY_ITEM_ID IS NOT NULL
)
GROUP BY BOM.ORGANIZATION_ID,
MSI.SEGMENT1,
MSIC.SEGMENT1
ORDER BY ORGANIZATION_CODE,
MSIC.SEGMENT1
My output is like this now:
I want to have another column which counts each item occurence in the output.

That's called conditional aggregation and you can do it with CASE EXPRESSION :
SELECT <col1>,<col2>..,
COUNT(CASE WHEN column ='Specific Val' THEN 1 END) as cnt
FROM YourTable
GROUP BY <col1>,<col2>..,
In MySQL this is also fine:
SUM(column = 'Specific Val')
Because expressions are treated as 1 and 0 .

wouldn't count(distinct column) work here since you're already using a group by

Would this be what you are looking for:
SELECT ColumnName, COUNT(*)
FROM TableName
GROUP BY ColumnName;
On the location of TableName you can also use a (SELECT ....).

You can also try this:
select count(*) from
(your query)
where column='value to be checked'

Related

Rewrite union query to a single query

I wondered if it was possible to write this as one query:
SELECT * FROM
(SELECT "markets".*
FROM "markets"
INNER JOIN "positions" ON "positions"."id" = "markets"."position_id"
INNER JOIN "players" ON "players"."id" = "positions"."player_id"
INNER JOIN "other_players" ON "other_players"."id" = "players"."other_player_id"
WHERE (markets.updated_at > '2021-01-10 11:50:14.136015')
AND "markets"."on_feed" = true
AND "markets"."deleted_at" IS NULL
UNION
SELECT "markets".*
FROM "markets"
WHERE (markets.updated_at > '2021-01-10 11:50:14.136015')
AND "markets"."on_feed" = true
AND "markets"."deleted_at" IS NOT NULL) results
ORDER BY results.updated_at DESC
There's a lot of overlap in the two queries. In the first one we want all markets that have those associations in place. For the 2nd query we don't really care if the associations are there or not.
SELECT * FROM "markets"
LEFT JOIN "positions" ON "positions"."id" = "markets"."position_id"
LEFT JOIN "players" ON "players"."id" = "positions"."player_id"
LEFT JOIN "other_players" ON "other_players"."id" = "players"."other_player_id"
WHERE markets.updated_at > '2021-01-10 11:50:14.136015'
AND "markets"."on_feed" = true
AND ("markets"."deleted_at" IS NOT NULL
OR "positions"."id" IS NOT NULL AND "players"."id" IS NOT NULL
AND "other_players"."id" IS NOT NULL)
ORDER BY markets.updated_at DESC
Use EXISTS instead:
SELECT m.*
FROM "markets" m
WHERE m.updated_at > '2021-01-10 11:50:14.136015' AND
markets."on_feed" = true AND
(m."deleted_at" IS NOT NULL OR
EXISTS (SELECT 1
FROM "positions" p JOIN
"players" pl
ON pl."id" = p."player_id" JOIN
"other_players" op
ON op."id" = p."other_player_id"
WHERE p."id" = m."position_id"
)
)
ORDER BY m.updated_at DESC

SQL sum and subtract issue with two queries

OK, I need somehelp. I have the following two queries:
SELECT DA.OWNG_OCD AS OFFICE, 'FL' AS STATE, SUM(S.STK_END_SEQ_NUM -
S.STK_STRT_SEQ_NUM) + COUNT(*) AS TOTSTK FROM STKRNG S, DFACCT DA, CMPNT C
WHERE RANGE_USED_SW = 'N' AND S.DFTACCT_CANUM = DA.DFTACCT_CANUM
AND DA.OWNG_OCD = C.OCD AND C.ST = 'FL' AND S.STK_TYP = 'R' GROUP
BY DA.OWNG_OCD;
AND
SELECT C.OCD, COUNT(*) AS USED FROM DRAFT D
JOIN STKRNG S ON S.DFTACCT_CANUM = D.DFTACCT_CANUM
JOIN DFACCT DA ON S.DFTACCT_CANUM = DA.DFTACCT_CANUM
JOIN CMPNT C ON CMPNT.OCD = DA.OWNG_OCD
WHERE D.DRFT_SEQ_NUM >= (SELECT MIN(S.STK_STRT_SEQ_NUM) FROM STKRNG S
WHERE D.DFTACCT_CANUM = S.DFTACCT_CANUM AND S.RANGE_USED_SW = 'N')
AND D.DRFT_SEQ_NUM <= (SELECT MAX(S.STK_END_SEQ_NUM) FROM STKRNG S WHERE
D.DFTACCT_CANUM = S.DFTACCT_CANUM AND S.RANGE_USED_SW = 'N')
AND S.STK_TYP = 'R'
AND S.RANGE_USED_SW = 'N'
AND C.ST = 'FL'
GROUP BY C.OCD;
I am trying to write one query where the results of the COUNT in the second query are subtracted from the results of the COUNT in the first query. Any idea on how to do this?
Put your queries in the from clause of your final query:
select q1.totstk - q2.used
from ( <your first query here> ) q1
join ( <your second query here> ) q2 on q2.ocd = q1.office;
try Something like this:
with STKRNGMINMAX as (
SELECT S.DFTACCT_CANUM,
MIN(S.STK_STRT_SEQ_NUM) MINNUM, MAX(S.STK_END_SEQ_NUM) MAXNUM,
SUM(S.STK_END_SEQ_NUM - S.STK_STRT_SEQ_NUM) DIFFNUM
FROM STKRNG S
WHERE (S.RANGE_USED_SW, S.STK_TYP)=('N', 'R')
group by S.DFTACCT_CANUM
)
SELECT C.OCD, S.DIFFNUM - COUNT(*) AS TOTSTK,
FROM DRAFT D
INNER JOIN STKRNGMINMAX S ON S.DFTACCT_CANUM = D.DFTACCT_CANUM and D.DRFT_SEQ_NUM between S.MINNUM AND S.MAXNUM
INNER JOIN DFACCT DA ON S.DFTACCT_CANUM = DA.DFTACCT_CANUM
INNER JOIN CMPNT C ON C.OCD = DA.OWNG_OCD and C.ST='FL'
GROUP BY C.OCD;

Convert Exist condition to Join with T-SQL

I am trying to convert the following T-SQL Select query to exclude "Exists" Clause and Include "Join" Clause. but i am ending up not getting the right result. can some one from this expert team help me with some tips.
select *
FROM HRData
INNER JOIN (
SELECT eeceeid, MIN(eecdateoftermination) eTermDate
FROM dbo.empcomp
INNER JOIN
(
SELECT xeeid FROM HRData_EEList
INNER JOIN dbo.empcomp t ON xeeid = eeceeid AND xcoid = eeccoid
WHERE eecemplstatus = 'T' AND eectermreason <> 'TRO' AND eeccoid <> 'WAON6'
AND EXISTS ( SELECT 1 FROM dbo.empded
INNER JOIN dbo.dedcode on deddedcode = eeddedcode AND deddedtype = 'MED' AND (eedbenstopdate IS NULL OR eedbenstopdate > '12/31/2005')
WHERE eedeeid = xeeid AND eedcoid = xcoid )
GROUP BY xeeid
HAVING COUNT(*) > 1) Term ON xeeid = eeceeid
group by eeceeid
) Terms ON eeid = eeceeid AND Termdate = eTermDate
The algorithm to convert EXISTS to JOIN is very simple.
Instead of
FROM A
WHERE EXISTS (SELECT *
FROM B
WHERE A.Foo = B.Foo)
Use
FROM A
INNER JOIN (SELECT DISTINCT Foo
FROM B) AS B
ON A.Foo = B.Foo
But the first one probably will be optimised better
Interesting request.
select *
FROM HRData
INNER JOIN (
SELECT eeceeid, MIN(eecdateoftermination) eTermDate
FROM dbo.empcomp
INNER JOIN
(
SELECT xeeid FROM HRData_EEList
INNER JOIN dbo.empcomp t ON xeeid = eeceeid AND xcoid = eeccoid
INNER JOIN
( SELECT DISTINCT xeeid, xcoid FROM dbo.empded
INNER JOIN dbo.dedcode on deddedcode = eeddedcode AND deddedtype = 'MED' AND (eedbenstopdate IS NULL OR eedbenstopdate > '12/31/2005')
-- WHERE eedeeid = xeeid AND eedcoid = xcoid
) AS A ON xeeid = A.xeeid AND eedcoid = A.eedcoid
WHERE eecemplstatus = 'T' AND eectermreason <> 'TRO' AND eeccoid <> 'WAON6'
GROUP BY xeeid
HAVING COUNT(*) > 1) Term ON xeeid = eeceeid
group by eeceeid
) Terms ON eeid = eeceeid AND Termdate = eTermDate
Another method of converting an exists to a join is to use a ROW_NUMBER() in the subselect to assist in removing duplicates.
EXISTS:
FROM A
WHERE EXISTS (SELECT *
FROM B
WHERE B.Condition = 'true' AND A.Foo = B.Foo)
JOIN:
FROM A
JOIN (SELECT B.Foo, ROW_NUMBER() OVER (PARTITION BY B.Foo ORDER BY B.Foo) RN
FROM B
WHERE B.Condition = 'true') DT
ON A.Foo = DT.Foo AND DT.RN = 1
The ORDER BY is totally arbitrary since you don't care which record it selects, but it's required. You may be able to use (SELECT NULL) instead.

Sum of resulting set of rows in SQL

I've got the following query:
SELECT DISTINCT CU.permit_id, CU.month, /*CU.year,*/ M.material_id, M.material_name, /*MC.chemical_id, C.chemical_name,
C.precursor_organic_compound, C.non_precursor_organic_compound,*/
/*MC.chemical_percentage,*/
POC_emissions =
CASE
WHEN (C.precursor_organic_compound = 'true')
THEN (CU.chemical_usage_lbs / CU.material_density) * M.VOC
ELSE 0
END,
NON_POC_emissions =
CASE
WHEN (C.non_precursor_organic_compound = 'true')
THEN CU.chemical_usage_lbs * (MC.chemical_percentage / 100)
ELSE 0
END
FROM material M
LEFT OUTER JOIN material_chemical MC ON MC.material_id = M.material_id
LEFT OUTER JOIN chemical_usage CU ON CU.material_id = MC.material_id
LEFT OUTER JOIN chemical C ON C.chemical_id = MC.chemical_id
WHERE (CU.month >=1 AND CU.month <= 2)
AND CU.year = 2013
AND M.material_id = 52
--AND CU.permit_id = 2118
--GROUP BY CU.permit_id, M.material_id, M.material_name, CU.month, MC.chemical_id, MC.chemical_id, C.chemical_name, C.precursor_organic_compound, C.non_precursor_organic_compound
--ORDER BY C.chemical_name ASC
Which returns:
But what I need is to return one row per month per material adding up the values of POC per month and NON_POC per month.
So, I should end up with something like:
Month material_id material_name POC NON_POC
1 52 Krylon... 0.107581 0.074108687
2 52 Krylon... 0.143437 0.0988125
I tried using SUM but it sums up the same result multiple times:
SELECT /*DISTINCT*/ CU.permit_id, CU.month, /*CU.year,*/ M.material_id, M.material_name, /*MC.chemical_id, C.chemical_name,
C.precursor_organic_compound, C.non_precursor_organic_compound,*/
--MC.chemical_percentage,
POC_emissions = SUM(
CASE
WHEN (C.precursor_organic_compound = 'true')
THEN (CU.chemical_usage_lbs / CU.material_density) * M.VOC
ELSE 0
END),
NON_POC_emissions = SUM(
CASE
WHEN (C.non_precursor_organic_compound = 'true')
THEN CU.chemical_usage_lbs * (MC.chemical_percentage / 100)
ELSE 0
END)
FROM material M
LEFT OUTER JOIN material_chemical MC ON MC.material_id = M.material_id
LEFT OUTER JOIN chemical_usage CU ON CU.material_id = MC.material_id
LEFT OUTER JOIN chemical C ON C.chemical_id = MC.chemical_id
WHERE M.material_id = 52
--AND CU.permit_id = 187
AND (CU.month >=1 AND CU.month <= 2)
AND CU.year = 2013
GROUP BY CU.permit_id, M.material_id, M.material_name, CU.month/*, CU.year, MC.chemical_id, C.chemical_name, C.precursor_organic_compound, C.non_precursor_organic_compound*/
--ORDER BY C.chemical_name ASC
The first query has a DISTINCT clause. What is the output without the DISTINCT clause. I suspect you have more rows than shows in your screenshot.
Regardless, you could try something like this to get the desired result.
select permit_id, month, material_id, material_name,
sum(poc_emissions), sum(non_poc_emissions)
from (
SELECT DISTINCT CU.permit_id, CU.month, M.material_id, M.material_name,
POC_emissions =
CASE
WHEN (C.precursor_organic_compound = 'true')
THEN (CU.chemical_usage_lbs / CU.material_density) * M.VOC
ELSE 0
END,
NON_POC_emissions =
CASE
WHEN (C.non_precursor_organic_compound = 'true')
THEN CU.chemical_usage_lbs * (MC.chemical_percentage / 100)
ELSE 0
END
FROM material M
LEFT OUTER JOIN material_chemical MC ON MC.material_id = M.material_id
LEFT OUTER JOIN chemical_usage CU ON CU.material_id = MC.material_id
LEFT OUTER JOIN chemical C ON C.chemical_id = MC.chemical_id
WHERE (CU.month >=1 AND CU.month <= 2)
AND CU.year = 2013
AND M.material_id = 52
) main
group by permit_id, month, material_id, material_name
Explanation
Since the results you retrieved by doing a DISTINCT was consider source-of-truth, I created an in-memory table by making it a sub-query. However, this subquery must have a name of some kind...whatever name. I gave it a name main. Subqueries look like this:
select ... from (sub-query) <give-it-a-table-name>
Simple Example:
select * from (select userid, username from user) user_temp
Advanced Example:
select * from (select userid, username from user) user_temp
inner join (select userid, sum(debits) as totaldebits from debittable) debit
on debit.userid = user_temp.userid
Notice how user_temp alias for the subquery can be used as if the sub-query was a real table.
Use above query in subquery and group by (month) and select sum(POC_emissions) and sum(NON_POC_emissions )

SQL - left join generate duplicates

I have code to select some applications but LEFT JOIN is creating duplicates.
Question: How to get rid of duplicates generated by
LEFT JOIN
attachments as att
ON
(a.ssn = att.ssn AND att.doc_type = 'id_copy' AND att.source = 'cpt3')
My Work:
I am considering SELECT DISTINCT or GROUP BY but, can't figure out where to put them.
This is the query:
SELECT
a.*, c.code, l.who, l.locked_time, att.id AS attnew,
( SELECT
COUNT(*)
FROM
applications as a2
WHERE
a2.approved = 'Y'
AND
a2.paid = 'Y'
AND
a2.paid_back = 'Y'
AND
a2.ssn = a.ssn
) AS previous_apps
FROM
applications as a
LEFT JOIN
locked_by as l
USING(id)
LEFT JOIN
campaign_codes as c
ON
c.id = a.campaign
LEFT JOIN
attachments as att
ON
(a.ssn = att.ssn AND att.doc_type = 'id_copy' AND att.source = 'cpt3')
WHERE
a.closed='N'
AND
a.paid = 'N'
ORDER BY
a.arrived_date
DESC
Use GROUP BY
to avoid duplicates. In your case:
...
WHERE
a.closed='N'
AND
a.paid = 'N'
GROUP BY
a.id
ORDER BY
a.arrived_date
DESC
If you want to make an one to one relation to avoid duplicates and att.id is the same for each att.ssn or you need only one (MAX,MIN,..ANY?) att.id. Try this:
LEFT JOIN
(SELECT ssn,
MAX(id) as id,
FROM attachments
WHERE doc_type = 'id_copy' AND source = 'cpt3'
GROUP BY ssn
)as att
ON
(a.ssn = att.ssn)