SQL: Get multiple line entries linked to one item? - sql

I have a table:
ID | ITEMID | STATUS | TYPE
1 | 123 | 5 | 1
2 | 123 | 4 | 2
3 | 123 | 5 | 3
4 | 125 | 3 | 1
5 | 125 | 5 | 3
Any item can have 0 to many entries in this table. I need a query that will tell me if an ITEM has all it's entries in either a state of 5 or 4. For example, in the above example, I would like to end up with the result:
ITEMID | REQUIREMENTS_MET
123 | TRUE --> true because all statuses are either 5 or 4
125 | FALSE --> false because it has a status of 3 and a status of 5.
If the 3 was a 4 or 5, then this would be true
What would be even better is something like this:
ITEMID | MET_REQUIREMENTS | NOT_MET_REQUIREMENTS
123 | 3 | 0
125 | 1 | 1
Any idea how to write a query for that?

Fast, short, simple:
SELECT itemid
,count(status = 4 OR status = 5 OR NULL) AS met_requirements
,count(status < 4 OR status > 5 OR NULL) AS not_met_requirements
FROM tbl
GROUP BY itemid
ORDER BY itemid;
Assuming all columns to be integer NOT NULL.
Builds on basic boolean logic:
TRUE OR NULL yields TRUE
FALSE OR NULL yields NULL
And NULL is not counted by count().
->SQLfiddle demo.

SELECT a.ID FROM (SELECT ID, MIN(STATUS) AS MINSTATUS, MAX(STATUS) AS MAXSTATUS FROM TABLE_NAME AS a GROUP BY ID)
WHERE a.MINSTATUS >= 4 AND a.MAXSTATUS <= 5

One way of doing this would be
SELECT t1.itemid, NOT EXISTS(SELECT 1
FROM mytable t2
WHERE itemid=t1.itemid
AND status NOT IN (4, 5)) AS requirements_met
FROM mytable t1
GROUP BY t1.itemid
UPDATE: for your updated requirement, you can have something like:
SELECT itemid,
sum(CASE WHEN status IN (4, 5) THEN 1 ELSE 0 END) as met_requirements,
sum(CASE WHEN status IN (4, 5) THEN 0 ELSE 1 END) as not_met_requirements
FROM mytable
GROUP BY itemid

simple one:
select
"ITEMID",
case
when min("STATUS") in (4, 5) and max("STATUS") in (4, 5) then 'True'
else 'False'
end as requirements_met
from table1
group by "ITEMID"
better one:
select
"ITEMID",
sum(case when "STATUS" in (4, 5) then 1 else 0 end) as MET_REQUIREMENTS,
sum(case when "STATUS" in (4, 5) then 0 else 1 end) as NOT_MET_REQUIREMENTS
from table1
group by "ITEMID";
sql fiddle demo

WITH dom AS (
SELECT DISTINCT item_id FROM items
)
, yes AS ( SELECT item_id, COUNT(*) AS good_count FROM items WHERE status IN (4,5) GROUP BY item_id
)
, no AS ( SELECT item_id, COUNT(*) AS bad_count FROM items WHERE status NOT IN (4,5) GROUP BY item_id
)
SELECT d.item_id
, COALESCE(y.good_count,0) AS good_count
, COALESCE(n.bad_count,0) AS bad_count
FROM dom d
LEFT JOIN yes y ON y.item_id = d.item_id
LEFT JOIN no n ON n.item_id = d.item_id
;
Can be done with an outer join, too:
WITH yes AS ( SELECT item_id, COUNT(*) AS good_count FROM items WHERE status IN (4,5) GROUP BY item_id)
, no AS ( SELECT item_id, COUNT(*) AS bad_count FROM items WHERE status NOT IN (4,5) GROUP BY item_id)
SELECT COALESCE(y.item_id, n.item_id) AS item_id
, COALESCE(y.good_count,0) AS good_count
, COALESCE(n.bad_count,0) AS bad_count
FROM yes y
FULL JOIN no n ON n.item_id = y.item_id
;

Nevermind, it was actually easy to do:
select ITEM_ID ,
sum (case when STATUS >= 3 then 1 else 0 end ) as met_requirements,
sum (case when STATUS < 3 then 1 else 0 end ) as not_met_requirements
from TABLE as d
group by ITEM_ID

Related

How to select data with group by and subquery calculations?

I have two tables:
list_table:
id
name
1
a
2
b
3
c
vt_table:
id
list_id
amount
direction_id
1
1
20
1
2
1
12
2
3
1
15
1
4
2
23
1
5
1
20
1
6
1
20
2
7
1
18
1
I need this result:
amount (dir_id = 1 - dir_id = 2), list_id
amount
list_id
41
1
23
2
0
3
Amount is sum of all amount fields in table vt_table where direction_id = 1 minus sum of all amount fileds in table vt_table where direction_id = 2
And I need group this calculations by list_id, and if table have no rows with list_id 3, as example, amount must be 0.
I'm trying to do it with this query:
SELECT vt.list_id
, ((SELECT COALESCE(SUM(vt.amount), 0)
FROM table_name vt
WHERE vt.direction_id = 1)
-
(SELECT COALESCE(SUM(vt.amount), 0)
FROM table_name vt
WHERE direction_id = 2)) AS result
FROM table_name vt
GROUP BY vt.list_id
But I don't know how to group it correctly and make it so that if there were no entries for some list_id, then the amount was 0 for this list_id.
I use PostgreSQL 12.
Here the examples
You can try to use OUTER JOIN with condition aggregate function with COALESCE fucntion.
Query 1:
SELECT l.id,
SUM(COALESCE(CASE WHEN vt.direction_id = 1 THEN vt.amount END,0)) -
SUM(COALESCE(CASE WHEN vt.direction_id = 2 THEN vt.amount END,0)) AS result
FROM table_name vt
RIGHT JOIN list l ON vt.list_id = l.id
GROUP BY l.id
ORDER BY l.id
Results:
| id | result |
|----|--------|
| 1 | 41 |
| 2 | 23 |
| 3 | 0 |
Try something like this, as a start:
SELECT vt.list_id
, COALESCE(SUM(CASE WHEN direction_id = 1 THEN amount END), 0)
- COALESCE(SUM(CASE WHEN direction_id = 2 THEN amount END), 0) AS result
FROM table_name vt
GROUP BY vt.list_id
;
Result using your fiddle:
list_id
result
1
41
2
23
This just misses the cases where there are no vt rows for some list.
Use an outer join to address those cases.
SELECT SUM(CASE WHEN vt.direction_id = 1 THEN vt.amount ELSE 0 END) - SUM(CASE WHEN vt.direction_id = 2 THEN vt.amount ELSE 0 END) as amount,
lt.id as list_id
FROM list_table lt
LEFT OUTER JOIN vt_table vt
ON lt.id = vt.list_id
GROUP BY lt.id
ORDER BY lt.id

How to return all records from table A , if any one of the column has a specific value in oracle sql?

Below is the sample data
If I pass lot name as a parameter, I want to return employees who has greater than 0 records in The specific Lot . Not just the one record but all the records of that employee.
Table A
Empid lotname itemcount
1 A 1
1 B 1
2 B 0
3 B 1
3 C 0
Parameter - B
Result :
Empid lotname itemcount
1 A 1
1 B 1
3 B 1
3 C 0
Because employee 3 and 1 has count in B lot. All the employee lot details should be returned.
select data.* from A data,
(select Empid,count(lotname)
from A
group by Empid
having count(lotname)>1) MulLotEmp
where data.lotname='B'
and data.Empid=MulLotEmp.Empid;
Check if this query solves your problem. In this I created a inner table first for your first requirement that emp with multiple lot, then I mapped this table with actual table with condition of input lot name.
If I understand correctly, you want all "1" and then only "0" if there is no "1".
One method is:
select a.*
from a
where itemcount = 1 or
not exists (select 1 from a a2 where a2.empid = a.empid and a2.itemcount = 1);
In Oracle, you can use the MAX analytic function:
SELECT Empid,
lotname,
itemcount
FROM (
SELECT t.*,
MAX( itemcount ) OVER ( PARTITION BY Empid ) AS max_itemcount
FROM table_name t
)
WHERE max_itemcount = 1;
So, for you sample data:
CREATE TABLE table_name ( Empid, lotname, itemcount ) AS
SELECT 1, 'A', 1 FROM DUAL UNION ALL
SELECT 1, 'B', 1 FROM DUAL UNION ALL
SELECT 2, 'B', 0 FROM DUAL UNION ALL
SELECT 3, 'B', 1 FROM DUAL UNION ALL
SELECT 3, 'C', 0 FROM DUAL;
This outputs:
EMPID | LOTNAME | ITEMCOUNT
----: | :------ | --------:
1 | A | 1
1 | B | 1
3 | B | 1
3 | C | 0
db<>fiddle here
The analytic function
sum(case when LOTNAME = 'B' /* parameter */ then ITEMCOUNT end) over (partition by EMPID) as lot_itemcnt
calculates for each customer the total number of items with the selected lot.
Feel free to use it as a bind variable, e.g.
sum(case when LOTNAME = ? /* parameter */ then ITEMCOUNT end) over (partition by EMPID) as lot_itemcnt
The whole query is than as follows
with cust as (
select
EMPID, LOTNAME, ITEMCOUNT,
sum(case when LOTNAME = 'B' /* parameter */ then ITEMCOUNT end) over (partition by EMPID) as lot_itemcnt
from tab)
select
EMPID, LOTNAME, ITEMCOUNT
from cust
where lot_itemcnt >= 1;

Finding orders where products of both types are present

Consider below table tbl:
ordernr productId productType
1 12 A
2 15 B
2 13 C
2 12 A
3 15 B
3 12 A
3 11 D
How can I get only rows where products of both productType's B and C are present in the order?
The desired output should be below because products of both type B and C are present in the order:
2 15 B
2 13 C
2 12 A
It might be more efficient to use use exists twice:
select t.*
from mytable t
where
exists (select 1 from mytable t1 where t1.ordernr = t.ordernr and t1.productid = 'B')
and exists (select 1 from mytable t1 where t1.ordernr = t.ordernr and t1.productid = 'C')
This query would take advantage of an index on (ordernr, productid).
One method is using a CTE to get the counts and then filter using those in the outer query:
WITH CTE AS(
SELECT ordernr,
productId,
productType
COUNT(CASE productType WHEN 'B' THEN 1 END) AS BCount,
COUNT(CASE productType WHEN 'C' THEN 1 END) AS CCount
FROM dbo.YourTable)
SELECT ordernr,
productId,
productType
FROM CTE
WHERE BCount > 0
AND CCount > 0;
You can get all the ordernrs that you need with this query:
select ordernr
from tablename
where productType in ('B', 'C')
group by ordernr
having count(distinct productType) = 2
So you can use it with the operator in:
select * from tablename
where ordernr in (
select ordernr
from tablename
where productType in ('B', 'C')
group by ordernr
having count(distinct productType) = 2
)
See the demo.
Results:
> ordernr | productId | productType
> ------: | --------: | :----------
> 2 | 15 | B
> 2 | 13 | C
> 2 | 12 | A

sql where have all multiple conditions in entry table

I've been trying to wrap my brain around this using joins, subquery joins, not exists clauses and I keep failing to come up with a query that produces the correct results.
I have 2 table's Trans and TransEntry
Trans Table (PRIMARY TransID)
TransID | Date
-----------
1 1/1/18
2 1/2/18
3 1/3/18
TransEntry Table (PRIMARY TransEntryID)
TransEntryID | TransID |Item
-----------
1 1 A
2 1 B
3 1 C
4 2 A
5 2 D
6 2 F
7 3 A
8 3 B
9 3 G
10 3 C
I need to have all TransID from TransEntry where the have item A and item C in the Entry, in our sample it will be only TransID (1,3)
or
SELECT TransID FROM TransEntry WHERE Item = 'A'
INTERSECT
SELECT TransID FROM TransEntry WHERE Item = 'C'
I think something like this should work:
SELECT TransID
FROM TransEntry
WHERE Item in ('A','C')
GROUP BY TransID
HAVING Count(DISTINCT Item) = 2;
Here's a sqlfiddle showing this
CREATE TABLE TransEntry(
TransEntryID INTEGER NOT NULL PRIMARY KEY
,TransID INTEGER NOT NULL
,Item VARCHAR(1) NOT NULL
);
INSERT INTO TransEntry(TransEntryID,TransID,Item) VALUES
(1,1,'A')
,(2,1,'B')
,(3,1,'C')
,(4,2,'A')
,(5,2,'D')
,(6,2,'F')
,(7,3,'A')
,(8,3,'B')
,(9,3,'G')
,(10,3,'C');
SELECT TransID
FROM TransEntry
WHERE Item in ('A','C')
GROUP BY TransID
HAVING Count(DISTINCT Item) = 2
+---------+
| TransID |
+---------+
| 1 |
| 3 |
+---------+
My answer wasn't as succinct as Aaron's, but in case it's helpful:
SELECT
TransID
FROM
Trans
WHERE
TransID IN ( SELECT TransID FROM TransEntry WHERE Item = 'A' ) AND
TransID IN ( SELECT TransID FROM TransEntry WHERE Item = 'C' )
you can go for something like this:
select transId
from
(
select
TransID ,
case when item = 'A' then 1 else 0 end as HasA,
case when item = 'C' then 1 else 0 end as HasC
from TransEntry
) a
group by transId
having sum(hasA) > 0 and sum(hasC) > 0
You can use GROUP BY :
SELECT TransID
FROM TransEntry
GROUP BY TransID
HAVING SUM(CASE WHEN item = 'A' THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN item = 'C' THEN 1 ELSE 0 END) > 0;
SELECT TransEntry.TransID, TransEntry.Item, GROUP_CONCAT(TransEntry.Item) AS items
FROM TransEntry
group by TransEntry.TransID Having Find_In_Set('A',items)>0 and Find_In_Set('C',items)>0
see the jsfiddle

How to GROUP BY in SQL and then mark as 0,1

I need to GROUP BY item_id and check if user_id in any of those matches a variable. If so, I want it to = 1, if not 0.
for example, imagine table like this:
item_id, user_id
1 1
1 3
2 4
2 1
2 7
2 3
3 4
3 6
4 8
4 1
5 3
IF (user_id = 3,1,0) AS match,
Want my Query to come back as
item_id, match
1 1
2 1
3 0
4 0
5 1
Where "1" all occurrences of user_id 3 in an item_id group.
You need the right aggregation function:
select item_id,
max(case when user_id = 3 then 1 else 0 end) as hasmatch
from t
group by item_id
order by item_id
In MySQL, true is 1 and false is 0, so you can just do:
SELECT item_id, MAX(user_id = 3) AS has_match
FROM table
GROUP BY 1
You can even count the number of matches:
SELECT item_id, SUM(user_id = 3) AS matches
FROM table
GROUP BY 1
GROUP BY 1 is short for GROUP BY item_id, as item_id is the first select expression.
I would do it as follows:
SELECT
A.item_id, ISNULL(B.count, 0)
FROM
(SELECT DISTINCT item_id 'item_id' FROM myTable) AS A
LEFT JOIN
(
SELECT item_id, count(*) 'count'
FROM myTable WHERE user_id IN (3, 1, 0)
GROUP BY item_id
) AS B
ON A.item_id = B.item_id