Conditional operations without using SWITCH CASE - sql

I have a couple of complex tables. But their mapping is something like:
TABLE_A:
_________________________________
| LINK_ID | TYPE_ID |
_________________________________
| adfasdnf23454 | TYPE 1 |
| 43fsdfsdcxsh7 | TYPE 1 |
| dfkng037sdfbd | TYPE 1 |
| sd09734fdfhsf | TYPE 2 |
| khsadf94u5dfc | TYPE 2 |
| piukvih66wfa8 | TYPE 3 |
_________________________________
TABLE_B:
_____________________________________________
| LINK_ID | CODE_ID | CODE_VALUE |
_____________________________________________
| adfasdnf23454 | 7 | test 1 |
| fgubk65esdfj7 | 6 | test 2 |
| ooogfsg354fds | 7 | test 3 |
| sd09734fdfhsf | 5 | test 4 |
_____________________________________________
The LINK_ID column links these two tables.
My requirement is to have all the records from TABLE_A checked whether they have a specific CODE_ID or not.
If the record has CODE_ID as 7 - populate CODE_VALUE in a column.
If the record has CODE_ID as 6 - populate CODE_VALUE in another column.
If the record doesn't have a CODE_ID show CODE_VALUE as null.
The catch is, TABLE_B may have records that TABLE_A don't. But the final result should contain the records of TABLE_A alone.
PS: SWITCH CASE not suggested since I would require the fields in the same row. (Please see the multiple rows for same LINK_ID in OBTAINED RESULT on using SWITCH CASE)
OBTAINED RESULT:
_______________________________________________
| LINK_ID | CODE_VALUE_1 | CODE_VALUE_1 |
_______________________________________________
| adfasdnf23454 | test 1 | null |
| adfasdnf23454 | null | test 4 |
| sd09734fdfhsf | test 6 | null |
_______________________________________________
EXPECTED RESULT:
__________________________________________________
| LINK_ID | CODE_VALUE_1 | CODE_VALUE_2 |
__________________________________________________
| adfasdnf23454 | test 1 | test 4 |
| 43fsdfsdcxsh7 | null | null |
| dfkng037sdfbd | null | null |
| sd09734fdfhsf | test 6 | null |
| khsadf94u5dfc | null | null |
| piukvih66wfa8 | null | null |
__________________________________________________
Can someone help on this?

One option uses two correlated subqueries:
select
a.link_id,
(select code_value from table_b b where b.link_id = a.link_id and b.code_id = 7) code_value_1,
(select code_value from table_b b where b.link_id = a.link_id and b.code_id = 6) code_value_2
from table_a a
Note that this assumes no duplicate (link_id, code_id) in table_b. You could also write this with two left joins - which is quite the same logic.
Another solution is a single left join, then conditional aggregation:
select
a.link_id,
max(case when b.code_id = 7 then b.code_value end) code_value_1,
max(case when b.code_id = 6 then b.code_value end) code_value_2
from table_a a
left join table_b b on b.link_id = a.link_id and b.code_id in (6, 7)
group by a.link_id

Problematic part of your question is what to do if two entries in B have same link_id and type_id. You can use min, max, last entry (but for that you need ordering column in B). Or you can list them all:
select *
from a left join b using (link_id)
pivot (listagg(code_value, ', ') within group (order by code_value)
for code_id in (6 as code6, 7 as code7))
Data:
create table a (link_id, type_id) as (
select 'a', 'TYPE 1' from dual union all
select 'b', 'TYPE 1' from dual union all
select 'c', 'TYPE 1' from dual union all
select 'd', 'TYPE 2' from dual );
create table b(LINK_ID, CODE_ID, CODE_VALUE) as (
select 'a', 6, 'test 1' from dual union all
select 'a', 7, 'test 2' from dual union all
select 'a', 7, 'test 3' from dual union all
select 'b', 7, 'test 4' from dual union all
select 'd', 6, 'test 5' from dual );
Result:
LINK_ID TYPE_ID CODE6 CODE7
a TYPE 1 test 1 test 2, test 3
b TYPE 1 test 4
c TYPE 1
d TYPE 2 test 5
dbfiddle

Related

Filtering a table via another table's values

I have 2 tables:
Value
+----+-------+
| id | name |
+----+-------+
| 1 | Peter |
| 2 | Jane |
| 3 | Joe |
+----+-------+
Filter
+----+---------+------+
| id | valueid | type |
+----+---------+------+
| 1 | 1 | A |
| 2 | 1 | B |
| 3 | 1 | C |
| 4 | 1 | D |
| 5 | 2 | A |
| 6 | 2 | C |
| 7 | 2 | E |
| 8 | 3 | A |
| 9 | 3 | D |
+----+---------+------+
I need to retrieve the values from the Value table where the related Filter table does not contain the type 'B' or 'C'
So in this quick example this would be only Joe.
Please note this is a DB2 DB and i have limited permissions to run selects only.
Or also a NOT IN (<*fullselect*) predicate:
Only that my result is 'Joe', not 'Jane' - and the data constellation would point to that ...
WITH
-- your input, sans reserved words
val(id,nam) AS (
SELECT 1,'Peter' FROM sysibm.sysdummy1
UNION ALL SELECT 2,'Jane' FROM sysibm.sysdummy1
UNION ALL SELECT 3,'Joe' FROM sysibm.sysdummy1
)
,
filtr(id,valueid,typ) AS (
SELECT 1,1,'A' FROM sysibm.sysdummy1
UNION ALL SELECT 2,1,'B' FROM sysibm.sysdummy1
UNION ALL SELECT 3,1,'C' FROM sysibm.sysdummy1
UNION ALL SELECT 4,1,'D' FROM sysibm.sysdummy1
UNION ALL SELECT 5,2,'A' FROM sysibm.sysdummy1
UNION ALL SELECT 6,2,'C' FROM sysibm.sysdummy1
UNION ALL SELECT 7,2,'E' FROM sysibm.sysdummy1
UNION ALL SELECT 8,3,'A' FROM sysibm.sysdummy1
UNION ALL SELECT 9,3,'D' FROM sysibm.sysdummy1
)
-- real query starts here
SELECT
*
FROM val
WHERE id NOT IN (
SELECT valueid FROM filtr WHERE typ IN ('B','C')
)
;
-- out id | nam
-- out ----+-------
-- out 3 | Joe
Or also, a failing left join:
SELECT
val.*
FROM val
LEFT JOIN (
SELECT valueid FROM filtr WHERE typ IN ('B','C')
) filtr
ON filtr.valueid = val.id
WHERE valueid IS NULL
You can use EXISTS, as in:
select *
from value v
where not exists (
select null from filter f
where f.valueid = v.id and f.type in ('B', 'C')
);
Result:
ID NAME
--- -----
3 Joe
See running example at db<>fiddle.

Count appearances of value in second column based on unique value in first column

The easiest way to explain this is given this table in Oracle SQL...
+-----------------+------------+
| COUNTRY | VALUE |
+-----------------+------------+
| England | A |
| England | A |
| England | A |
| England | B |
| England | B |
| France | A |
| France | A |
| France | B |
+-----------------+------------+
how would I produce this result, which is the count of A's and B's for the unique values in column COUNTRY
+-----------+------------+------------+
| COUNTRY | COUNT(A) | COUNT(B) |
+-----------+------------+------------+
| England | 3 | 2 |
| France | 2 | 1 |
+-----------+------------+------------+
I'm sure this has already been answered, I just don't know how to ask the question.
Thanks
select country,
sum( case when value = 'A' then 1 else 0 end ) numA,
sum( case when value = 'B' then 1 else 0 end ) numB
from table
group by country
is one example of conditional aggregation
Use PIVOT:
Oracle Setup:
CREATE TABLE table_name ( COUNTRY, VALUE ) AS
SELECT 'England', 'A' FROM DUAL UNION ALL
SELECT 'England', 'A' FROM DUAL UNION ALL
SELECT 'England', 'A' FROM DUAL UNION ALL
SELECT 'England', 'B' FROM DUAL UNION ALL
SELECT 'England', 'B' FROM DUAL UNION ALL
SELECT 'France', 'A' FROM DUAL UNION ALL
SELECT 'France', 'A' FROM DUAL UNION ALL
SELECT 'France', 'B' FROM DUAL;
Query:
SELECT *
FROM table_name
PIVOT ( COUNT(*) FOR value IN ( 'A' AS "COUNT(A)", 'B' AS "COUNT(B)" ) )
Output:
COUNTRY | COUNT(A) | COUNT(B)
:------ | -------: | -------:
England | 3 | 2
France | 2 | 1
db<>fiddle here

group by conditional on two columns in hibernate

I want to group by on two columns. I want to get total of c group by a and b if b is not null and group by a if b is null
I wrote this query but it does not work in case b is null!the result of query is all rows that b is not null
select m.s.a ,
case when (m.l is not null)
then m.l.code end , coalesce(sum(m.c),0 )
from material m where m.Item.id =:itemId
group by m.s.a, case
when (m.l is not null)
then m.l.code end
+--+----+-------+---+
| | s | l | c |
+--+----+-------+---+
| | a | d | 1 |
| | a | d | 9 |
| | a | e | 3 |
| | a | f | 4 |
| | c | g | 5 |
| | c | g | 6 |
| | c | h | 20 |
| | d | null | 7 |
| | d | null | 8 |
result expected:
+--+----+-------+---+
| | s | l | c |
+--+----+-------+---+
| | a | d | 10 |
| | a | e | 3 |
| | a | f | 4 |
| | c | g | 11 |
| | c | h | 20 |
| | d | | 15 |
By default, oracle/postgres/mysql will produces the expected output.
SELECT s,l,sum(c)
FROM temp
GROUP BY s,l;
If you don't want to group by NULL values you can use UNION
SELECT s,l,sum(c)
FROM temp
WHERE l is NOT NULL
GROUP BY s,l
UNION
SELECT s,l,sum(c)
FROM temp
WHERE l is NULL;
with data (col1, col2, val) as
(
select 'a', 'd', 1 from dual union all
select 'a', 'd', 9 from dual union all
select 'a', 'e', 3 from dual union all
select 'a', 'f', 4 from dual union all
select 'c', 'g', 5 from dual union all
select 'c', 'g', 6 from dual union all
select 'c', 'h', 20 from dual union all
select 'd', null, 7 from dual union all
select 'd', null, 8 from dual union all
select 'e', 'g', null from dual -- additional check if val is null
)
,
prs (col1, col2, col1n2) as
(
select distinct col1, col2, col1||'-'||col2 from data
)
,
rs (col, val) as
(
-- concatenate the columns that need to be grouped by
-- to act as one single column (col1 and col2)
select col1||'-'||col2, sum(nvl(val,0)) from data group by col1||'-'||col2
)
select
prs.col1, prs.col2, rs.val
from
rs join prs
on (prs.col1n2 = rs.col)
order by 1
;

How can I omit groups of records where the attribute is based on a 'like' clause across 2 different fields

I need a query to omit groups where the number 16 is present in both records and are present across different attributes within the group . Basically, if we have a 16 somewhere in attributes on different records, then we know what accounts for these groups, and no further analysis is needed on them. We would like to keep results where 16 only occurs in one record in either attribute, 16 occurs in neither, and records that have nulls in them but do not have the 16 in 2 records in different attributes.
Here is an example:
---------------------------------------------
| groupid | category | test_results |
---------------------------------------------
| 001 | red13tall | |
| 001 | | blue16small |
| 002 | green16small| |
| 002 | | blue16small |
| 003 | yellow3tall | |
| 003 | | green2giant |
| 004 | orange16tall | |
| 004 | | blue16tall |
| 005 | red16short | |
| 005 | green12bald | orange14tall |
| 006 | blue3short | red16big |
| 006 | green16flat | |
---------------------------------------------
This is the result we are looking for:
---------------------------------------------
| groupid | category | test_results |
---------------------------------------------
| 001 | red13tall | |
| 001 | | blue16small |
| 003 | yellow3tall | |
| 003 | | green2giant |
| 005 | red16short | |
| 005 | green12bald | orange14tall |
------------------------------------------
Assuming your table is called your_table and has a primary key of id, then
SELECT t3.groupid, t3.category, t3.test_results
FROM your_table t3
WHERE t3.groupid NOT IN (
SELECT t1.groupid
FROM your_table t1, your_table t2
WHERE t1.id <> t2.id
AND t1.groupid = t2.groupid
AND t1.category LIKE '%16%'
AND t2.test_results LIKE '%16%'
)
Note, this assumes you're looking for 16 to appear in two different rows in the 2 different columns. If you don't care if they appear in the same row then you can remove the t1.id <> t2.id condition.
One way or another you need conditional counting. If you use analytic functions you can avoid joins, which are often a performance drag.
For the solution below I interpreted your words literally: each group has exactly two rows, and a group is excluded if all three conditions are met: BOTH rows have 16 at least once (in category or in test_results); 16 appears in category at least once; and 16 appears in test_results at least once.
You can modify the query very easily if you don't need the condition on each row of the group having 16 at least once (remove all references to r_ct).
with
test_data ( groupid, category, test_results ) as (
select '001', 'red13tall' , null from dual union all
select '001', null , 'blue16small' from dual union all
select '002', 'green16small', null from dual union all
select '002', null , 'blue16small' from dual union all
select '003', 'yellow3tall' , null from dual union all
select '003', null , 'green2giant' from dual union all
select '004', 'orange16tall', null from dual union all
select '004', null , 'blue16tall' from dual union all
select '005', 'red16short' , null from dual union all
select '005', 'green12bald' , 'orange14tall' from dual union all
select '006', 'blue3short' , 'red16big' from dual union all
select '006', 'green16flat' , null from dual
)
-- end of test data (not part of solution); SQL query begins below this line
select groupid, category, test_results
from (
select groupid, category, test_results,
count(case when category like '%16%' then 1
when test_results like '%16%' then 1 end)
over (partition by groupid) as r_ct,
count(case when category like '%16%' then 1 end)
over (partition by groupid) as c_ct,
count(case when test_results like '%16%' then 1 end)
over (partition by groupid) as t_ct
from test_data
)
where r_ct < 2 or c_ct = 0 or t_ct = 0
order by groupid -- if needed
;
Output:
GROUPID CATEGORY TEST_RESULTS
------- ------------ ------------
001 red13tall
001 blue16small
003 yellow3tall
003 green2giant
005 red16short
005 green12bald orange14tall
6 rows selected.

Populate SQL Data

I have following data from SQL
| SR.NO | ATTR-X | ATTR-Z |
---------------------------------------
| 1 | A | a1 |
| 2 | B | a2 |
| 3 | C | a3 |
| 4 | A | a4 |
---------------------------------------
I want this to
| SR | A | B | C | ATTR-Z |
----------------------------------
| 1 | A | - | - | a1 |
| 2 | - | B | - | a2 |
| 3 | - | - | C | a3 |
| 4 | A | - | - | a4 |
----------------------------------
Can we do it in SQL queries itself?
Use a CASE statement to determine what is needed in each column.
SELECT SR_NO, CASE WHEN [Attribute -X] = 'A' THEN A ELSE NULL END AS 'A',
CASE WHEN [Attribute -X] = 'B' THEN B ELSE NULL END AS 'B',
CASE WHEN [Attribute -X] = 'C' THEN C ELSE NULL END AS 'C',
[Attribute -Z] AS 'ATTR-Z'
FROM yourtable
Could you clarify a bit further? Do you have that Data in SQL and want it populated in your HTML form?
Or
Did you mean you just want the exact same data to show up in an SQL Query
If just a simple query can do this
select 1 as SR_NO, 'A' as A,'-' as B , '-' as C, 'a1' as [ATTR-Z]
union all
select 2 as SR_NO, '-' as A,'B' as B , '-' as C, 'a2' as [ATTR-Z]
union all
select 3 as SR_NO, 'A' as A,'-' as B , '-' as C, 'a3' as [ATTR-Z]
union all
select 4 as SR_NO, '-' as A,'-' as B , 'C' as C, 'a4' as [ATTR-Z]
However this isn't really useful for anything other than displaying the table you just showed - if you want it to follow a certain pattern/cases then you'd need to let us know more about what you'd want.