sql where have all multiple conditions in entry table - sql

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

Related

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

how to remove records when orderid is repeated

OrderId InvoiceNumber LocationId
1 A1 1
1 B1 2
1 0
2 0
3 C1 2
3 0
Expected Output
OrderId InvoiceNumber LocationId
1 A1 1
1 B1 2
2 0
3 C1 2
In above Result....i want to remove those recored which have InvoiceNumber is null and LocationId is 0 when orderId is repeated in query.
In above Example....OrderId=1 record is repeated so then check InvoiceNuber is null and LocationId is 0 if found then removed it
OrderId=2 is not repeated so keep the record....
I tried this
Select * from tblName where isnull(InvoiceNuber,'')='' and LocationId=0
But...this query is wrong...
Thank uuu...!!..
You can use window functions:
select *
from (
select t.*, count(InvoiceNumber) over(partition by orderId) cnt
from mytable t
) t
where not (InvoiceNumber is null and locationId = 0 and cnt > 0)
This evicts rows where InvoiceNumber is null and locationId is 0 for which another row exists with the same orderId and a non-null InvoiceNumber.

SQL Query with group and having

I'm trying to select all client ID's that has TypeId equal 1 but not TypeId equal 3.
Table example:
---------------------
| ClientID | TypeId |
---------------------
| 1 | 1 |
| 1 | 3 |
| 2 | 3 |
| 3 | 1 |
---------------------
My query:
SELECT ClientId, TypeId
FROM Table
GROUP BY ClientId, TypeId
HAVING TypeId != 3
What I have:
---------------------
| ClientID | TypeId |
---------------------
| 1 | 1 |
| 3 | 1 |
---------------------
What I expect:
---------------------
| ClientID | TypeId |
---------------------
| 3 | 1 |
---------------------
The critical thing is that the table have more than 3 * 10^8 registers.
Thanks in advance!
I would suggest aggregation and having:
SELECT ClientId
FROM Table
GROUP BY ClientId
HAVING SUM(CASE WHEN TypeId = 1 THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN TypeId = 3 THEN 1 ELSE 0 END) = 0;
Each condition in the HAVING clause counts the number of rows having a particular TypeId value. The > 0 means there is at least one. The = 0 means there are none.
If you actually want to get the original rows that match -- so all TypeIds associated with a client. You can use a JOIN or window functions:
SELECT ClientId, TypeId
FROM (SELECT ClientId, TypeId,
SUM(CASE WHEN TypeId = 1 THEN 1 ELSE 0 END) OVER (PARTITION BY ClientId) as TypeId_1,
SUM(CASE WHEN TypeId = 3 THEN 1 ELSE 0 END) OVER (PARTITION BY ClientId) as TypeId_3
FROM Table
) t
WHERE TypeId_1 > 0 AND TypeId_3 = 0;
Try this:
SELECT t1.*
FROM Table AS t1
WHERE TypeId = 1 AND
NOT EXISTS (SELECT 1
FROM Table AS t2
WHERE t1.ClientId = t2.ClientId AND t2.TypeId = 3)
Try this one
SELECT ClientId, TypeId
FROM Table
WHERE ClientId not in (select ClientId from Table where TypeId = 3)
GROUP BY ClientId, TypeId
I think you could also achieve this with a common table expression:
WITH cte AS (
SELECT ClientId
FROM [Table]
WHERE TypeId = 1
)
SELECT DISTINCT ClientId
FROM [Table]
WHERE ClientId IN
(
SELECT ClientId
FROM cte
)
AND TypeId != 3
Alternatively, try this:
WITH cte AS (
SELECT ClientId
FROM [Table]
WHERE TypeId = 1
)
SELECT ClientId
FROM cte
EXCEPT
SELECT CLientId
FROM [Table]
WHERE TypeId = 3

SQL: Get multiple line entries linked to one item?

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