How to group by and select - sql

I tried to extract customer who has type a
I guess I must group by in customer and tried to having in type
customer type
A a
A c
B b
B c
C a
C a
but I couldn't figure out specific way to achieve this.
If someone has opinion,please let me know.
My desired result is following
customer type
A a
A c
C a
C a
Thanks

Using exists, we can try:
SELECT t1.customer, t1.type
FROM yourTable t1
WHERE EXISTS (SELECT 1 FROM yourTable t2 WHERE t2.customer = t1.customer AND t2.type = 'a');
The exists logic reads in plain English as select any record for which we can find at least one record for the same customer whose type is a. This means retain all customer records, where at least one of those records has type a.

You don't need to group. You can just filter the table for customers that have type 'a' and use that resultset to filter the table again for the customers.
WITH
cust_data
AS
(SELECT 'A' AS customer, 'a' AS TYPE FROM DUAL
UNION ALL
SELECT 'A' AS customer, 'c' AS TYPE FROM DUAL
UNION ALL
SELECT 'B' AS customer, 'b' AS TYPE FROM DUAL
UNION ALL
SELECT 'B' AS customer, 'c' AS TYPE FROM DUAL
UNION ALL
SELECT 'C' AS customer, 'a' AS TYPE FROM DUAL
UNION ALL
SELECT 'C' AS customer, 'a' AS TYPE FROM DUAL)
SELECT *
FROM cust_data c
WHERE customer IN (SELECT customer
FROM cust_data
WHERE TYPE = 'a');
This gives
CUSTOMER TYPE
A c
A a
C a
C a

I hope the below query completes your requirement.
SELECT
*
FROM
test.customer
WHERE
customer IN (SELECT
customer
FROM
test.customer
WHERE
type = 'a');
Output:
A a
A c
C a
C a

Do not use accessing table twice. Use window functions instead.
with t(customer, type) as (
select 'A', 'a' from dual union all
select 'A', 'c' from dual union all
select 'B', 'b' from dual union all
select 'B', 'c' from dual union all
select 'C', 'a' from dual union all
select 'C', 'a' from dual)
select customer, type
from
(select t.*, count(decode(type, 'a', 1)) over (partition by customer) cnt
from t
)
where cnt > 0;
CUSTOMER TYPE
-------- ----
A a
A c
C a
C a

Related

Oracle SQL - Count based on a condition to include distinct rows with zero matches

Is there a "better" way to refactor the query below that returns the number occurrences of a particular value (e.g. 'A') for each distinct id? The challenge seems to be keeping id = 2 in the result set even though the count is zero (id = 2 is never related to 'A'). It has a common table expression, NVL function, in-line view, distinct, and left join. Is all of that really needed to get this job done? (Oracle 19c)
create table T (id, val) as
select 1, 'A' from dual
union all select 1, 'B' from dual
union all select 1, 'A' from dual
union all select 2, 'B' from dual
union all select 2, 'B' from dual
union all select 3, 'A' from dual
;
with C as (select id, val, count(*) cnt from T where val = 'A' group by id, val)
select D.id, nvl(C.cnt, 0) cnt_with_zero from (select distinct id from T) D left join C on D.id = C.id
order by id
;
ID CNT_WITH_ZERO
---------- -------------
1 2
2 0
3 1
A simple way is conditional aggregation:
select id,
sum(case when val = 'A' then 1 else 0 end) as num_As
from t
group by id;
If you have another table with one row per id, you I would recommend:
select i.id,
(select count(*) from t where t.id = i.id and t.val = 'A') as num_As
from ids i;

How to compare column in one table with array from another table in BigQuery?

Just continue from the answer for my previous question.
I want to get all values from table b (in rows) if there is any difference between values in arrays from table a by same ids
WITH a as (SELECT 1 as id, ['123', 'abc', '456', 'qaz', 'uqw'] as value
UNION ALL SELECT 2, ['123', 'wer', 'thg', '10', '200']
UNION ALL SELECT 3, ['200']
UNION ALL SELECT 4, null
UNION ALL SELECT 5, ['140']),
b as (SELECT 1 as id, '123' as value
UNION ALL SELECT 1, 'abc'
UNION ALL SELECT 1, '456'
UNION ALL SELECT 1, 'qaz'
UNION ALL SELECT 1, 'uqw'
UNION ALL SELECT 2, '123'
UNION ALL SELECT 2, 'wer'
UNION ALL SELECT 2, '10'
UNION ALL SELECT 3, null
UNION ALL SELECT 4, 'wer'
UNION ALL SELECT 4, '234'
UNION ALL SELECT 5, '140'
UNION ALL SELECT 5, '121'
)
SELECT * EXCEPT(flag)
FROM (
SELECT b.*, COUNTIF(b.value IS NULL) OVER(PARTITION BY id) flag
FROM a LEFT JOIN a.value
FULL OUTER JOIN b
USING(id, value)
)
WHERE flag > 0
AND NOT id IS NULL
It works well for all ids except 5.
In my case I need to return all values if there is any difference.
In example array with id 5 from table a has only one value is '140' while there are two rows with values by id 5 from table b. So in this case all values by id 5 from table b also must appear in expected output
How need to modify this query to get what I want?
UPDATED
Seems like it works for me. But I can not be sure for 100%
SELECT * EXCEPT(flag)
FROM (
SELECT b.*, COUNTIF((b.value IS NULL AND a.value IS NOT NULL) OR (b.value IS NOT NULL AND a.value IS NULL)) OVER(PARTITION BY id) flag
FROM a LEFT JOIN a.value
FULL OUTER JOIN b
USING(id, value)
)
WHERE flag > 0
AND NOT id IS NULL
#standardSQL
SELECT *
FROM table_b
WHERE id IN (
SELECT id FROM table_a a
JOIN table_b b USING(id)
GROUP BY id
HAVING STRING_AGG(IFNULL(b.value, 'NULL') ORDER BY b.value) !=
IFNULL(ANY_VALUE((SELECT STRING_AGG(IFNULL(value, 'NULL') ORDER BY value) FROM a.value)), 'NULL')
)

Determine SQL From clause at runtime?

I have a table1 which contains a column where it stores other table's name.
Based on the value in table1, the query should pull out data corresponding to the table name given in it.
For example , let the table which stores tablename be tablelist(tablename,tableid)
Let the other tables whose names stored in tablelist.tablename be A, B , C
Based on a given input parameter tableid,
If the value stored in tablename is 'A' the query should pull out results equivalent to :
Select A.name from A;
If its 'B', the query should be :
Select B.type from B;
If its 'C' , the query should be :
Select C.msg from C;
How will I make it into a single query which accepts the table id as input ?
Please advice
You could try case when construction:
select case tableid
when 'A' then (select name from a)
when 'B' then (select type from b)
when 'C' then (select msg from c)
end
from tbl
Example with some data:
with
tablelist(tablename, tableid) as (
select 'A', 1 from dual union all
select 'B', 2 from dual union all
select 'B', 7 from dual union all
select 'C', 3 from dual ),
a(name) as (select 'Chris' from dual),
b(type) as (select 'T800' from dual),
c(msg) as (select 'Hello' from dual)
select case tablename
when 'A' then (select name from a)
when 'B' then (select type from b)
when 'C' then (select msg from c)
end as value
from tablelist
where tableid = 7
Result T800.

Why do I still get duplicates on [CONMAT_MATCHING_DONOR] even after using DISTINCT?

I still get duplicates on [CONMAT_MATCHING_DONOR] even after using DISTINCT.
SELECT TOP 1000 [CONTRIB_MATCH_ID]
,[CONMAT_CONTRIBUTION]
,[CONMAT_FORM_RECEIVED_DATE]
,[CONMAT_MATCHING_DONOR]
,[CONMAT_STATUS]
,[STATUS_DESC]
,[CONMAT_STATUS_DATE]
FROM [ods_production].[dbo].[SPT_CONTRIB_MATCH]
WHERE [CONMAT_MATCHING_DONOR] IN (SELECT DISTINCT
[CONMAT_MATCHING_DONOR]
FROM [ods_production].[dbo].[SPT_CONTRIB_MATCH])
ORDER BY [CONMAT_MATCHING_DONOR] DESC
Your usage of DISTINCT in the IN clause doesn't make much sense - this will not affect the results of your query in any way.
Consider:
WITH v_base(name) AS (
SELECT 'A' UNION ALL
SELECT 'A' UNION ALL
SELECT 'B')
SELECT name FROM v_base WHERE name IN (SELECT DISTINCT name from v_base)
which more or less translates to
WITH v_base(name) AS (
SELECT 'A' UNION ALL
SELECT 'A' UNION ALL
SELECT 'B')
SELECT name FROM v_base WHERE name IN ('A', 'B')
vs
WITH v_base(name) AS (
SELECT 'A' UNION ALL
SELECT 'A' UNION ALL
SELECT 'B')
SELECT DISTINCT name FROM v_base WHERE name IN (SELECT name from v_base)
which translates to
WITH v_base(name) AS (
SELECT 'A' UNION ALL
SELECT 'A' UNION ALL
SELECT 'B')
SELECT DISTINCT name FROM v_base WHERE name IN ('A', 'A', 'B')
and the difference should become clear.
SQL Fiddle

How to use decode or case function to build a condition base on the result of two records?

Let's say if I search by a key, it returns 2 records with 2 different values for each record.
It will return value 'A' and value 'B' for the 1st and 2nd records respectively.
ID VALUE
1 A
1 B
If the returned records contains 'A' and 'B' then I want to change all their value to 'C'.
If the returned record only contains 'A' or 'B' then i don't want to change to 'C'
How do i use the decode or case function to do that?
I tried (Case when value in('A','B') then 'C' else value end)
but it also changes the records that only returns either 'A' or 'B' to 'C'
So basically if my result are like this :
ID VALUE
1 A
1 B
I want it to be like this
ID VALUE
1 C
1 C
If the result is
ID VALUE or ID VALUE
1 A 1 B
1 A 1 B
Then don't implement the above conversion rule.
Edit for clarity
select id, value from t1
where id =123
gives me below
ID VALUE
1 A
1 B
I want a condition that uses the value of the two records--change the value to 'C' only when clm1.value=A and clm2.value=B
something like below but it does not work.
select id,
case when value ='A' and value ='B' then 'C' else value end
from t1
where id=123
Sorry for the confusion.
Thanks
What about something like this:
create table csm (id int, value varchar(5))
insert into csm (id,value)
SELECT 1,'A' UNION
SELECT 1,'B' UNION
SELECT 2,'A' UNION
SELECT 3,'B' UNION
SELECT 4,'A' UNION
SELECT 4,'B' UNION
SELECT 4,'D'
SELECT t.id
, case when tsub.TotalTimes=2 AND tsub.NumTimes=2 THEN 'C' ELSE t.value END as Value
FROM csm t
JOIN (
SELECT id, COUNT(DISTINCT CASE WHEN value IN ('A','B') THEN value END) AS NumTimes
, COUNT(DISTINCT value) TotalTimes
FROM csm
GROUP BY id
) AS tsub ON t.id=tsub.id
I get the following output:
1 C
1 C
2 A
3 B
4 A
4 B
4 D
The subquery finds out the number of times A and B occur for that id, and then your case statement checks if that value is 2, and if so changes it to C.
Seems like a perfect match for an analytic function:
with v_data(id, value) as (
select 1, 'A' from dual union all
select 1, 'B' from dual union all
select 2, 'A' from dual union all
select 3, 'B' from dual union all
select 3, 'B' from dual
)
select
v1.*,
(case
when v1.cnt_distinct > 1 then 'C'
else v1.value end)
as new_value
from (
select
id,
value,
count(*) over (partition by id) as cnt_overal,
count(distinct value) over (partition by id) as cnt_distinct
from v_data)
v1
This computes the number of distinct values for each ID (using count(distinct...) and then replaces the values with C if the number of distinct values is larger than 1.