How to do pivot in plsql - sql

I have a data like this
Col_A Col_B Col_C Value Col_E
Id_1 String_a type1 null Flat
Id_1 String_a type2 30 Not Flat
Id_1 String_b type1 null Flat
Id_1 String_b type2 30 Not Flat
Id_2 String_c type1 35 Flat
Id_2 String_c type2 55 Flat
I want the output to be like below
Col_A type1_Col_E type1 type1_count type2_Col_E type_2 type2_count
Id_1 Flat null 0 Not Flat 30 2
Id_2 Flat 35 1 Flat 35 1
I need to have all the types as column and need counts as another column for each of its type. So if I have 2 strings associated to one Id, I should have the count as 2 in its type. I need to have all the types as column and I need to have the count to indicate how many strings got associated to each of the type. If the Value is null, I should consider the count as zero.
Is it possible to achieve this with PL SQL?

You don't need PL/SQL, but SQL Select statement with correlated subqueries as below best suits for your case as below :
with tab( Col_A, Col_B, Col_C, Value, Col_E ) as
(
select 'Id_1','String_a','type1',null,'Flat' from dual union all
select 'Id_1','String_a','type2', 30,'Not Flat' from dual union all
select 'Id_1','String_b','type1',null,'Flat' from dual union all
select 'Id_1','String_b','type2', 30,'Not Flat' from dual union all
select 'Id_2','String_c','type1', 35,'Flat' from dual union all
select 'Id_2','String_c','type2', 55,'Flat' from dual
)
select Col_A,
( select distinct Col_E from tab where Col_A = t.Col_A and Col_C = 'type1' )
as type1_Col_E,
( select distinct value from tab where Col_A = t.Col_A and Col_C = 'type1' )
as type1,
( select sum(nvl2(value,1,0)) from tab where Col_A = t.Col_A and Col_C = 'type1' )
as type1_count,
( select distinct Col_E from tab where Col_A = t.Col_A and Col_C = 'type2' )
as type2_Col_E,
( select distinct value from tab where Col_A = t.Col_A and Col_C = 'type2' )
as type2,
( select sum(nvl2(value,1,0)) from tab where Col_A = t.Col_A and Col_C = 'type2' )
as type2_count
from tab t
group by t.Col_A;
COL_A TYPE1_COL_E TYPE1 TYPE1_COUNT TYPE2_COL_E TYPE2 TYPE2_COUNT
----- ----------- ------- ----------- ----------- ------ -----------
Id_1 Flat null 0 Not Flat 30 2
Id_2 Flat 35 1 Flat 35 1

Related

Find value with same value in string ORACLE

We have a table like this:
Column A
Column B
Column C
Cell 1
201453
1000
Cell 2
201232
1000
Cell 3
213231
2000
Cell 2
201233
3000
Cell 1
200032
1000
Column A - may be repeated
Column B - unique
Column C - may be repeated
How do I find value (column A), which have same value (column C)?
I don't get it...
If I'm getting the request right, you need to know each value os columnA that has the same value in ColumnC:
select LISTAGG("Column A",',') WITHIN GROUP (ORDER BY "Column A") from table1
group by "Column C"
test
http://sqlfiddle.com/#!4/303780/5
Apparently, you need to count the duplicates for values of col_a vs. col_c pair . So, using a HAVING clause along with grouping by those two columns will do the trick such as
SELECT col_a
FROM t
GROUP BY col_a,col_c
HAVING COUNT(*) > 1
Demo
If you are looking for rows having the same (repeated) combination of COL_A and COL_C then try this:
WITH
tbl AS
(
Select 1 "COL_A", 201453 "COL_B", 1000 "COL_C" From Dual UNION ALL
Select 2 "COL_A", 201232 "COL_B", 1000 "COL_C" From Dual UNION ALL
Select 3 "COL_A", 213231 "COL_B", 2000 "COL_C" From Dual UNION ALL
Select 2 "COL_A", 201233 "COL_B", 3000 "COL_C" From Dual UNION ALL
Select 1 "COL_A", 200032 "COL_B", 1000 "COL_C" From Dual
)
SELECT
COL_A, COL_B, COL_C
FROM
( Select COL_A, COL_B, COL_C,
Count(COL_B) OVER(Partition By COL_A, COL_C) "COUNT_A_C"
From tbl )
WHERE COUNT_A_C > 1
R e s u l t :
COL_A COL_B COL_C
---------- ---------- ----------
1 201453 1000
1 200032 1000
Used analytic function Count() Over() - more about analytic functions here.
... Or with aggregate function Count(), Group By and Having - the result is the same as above
SELECT t.COL_A, t.COL_B, t.COL_C
FROM tbl t
INNER JOIN
( Select COL_A, COL_C, Count(COL_B) "COUNT_A_C"
From tbl
Group By COL_A, COL_C
Having Count(COL_B) > 1) c ON (c.COL_A = t.COL_A And c.COL_C = t.COL_C)

An efficient way to backward fill group values by type

I have a logs table with id, name, type, and date columns. And I want to create a new column which represents next id value from the 2nd type. Query must be grouped by name and only type1 values filled based on next type2 value.
logs table:
id
name
type
date
1
name1
type1
2022-01-01
2
name1
type1
2022-01-02
3
name1
type2
2022-01-03
4
name1
type1
2022-01-03
5
name1
type2
2022-01-04
6
name1
type1
2022-01-05
7
name2
type1
2022-01-03
8
name2
type2
2022-01-08
desirable output:
id
name
type
date
type2_id
1
name1
type1
2022-01-01
3
2
name1
type1
2022-01-02
3
4
name1
type1
2022-01-03
5
6
name1
type1
2022-01-05
7
name2
type1
2022-01-03
8
I was able to do it using joins and LAG function. However, maybe someone could help me make it more efficient?
WITH logs AS (
SELECT 1 AS id, 'name1' AS name, 'type1' AS type, '2022-01-01' AS date,
UNION ALL
SELECT 2 AS id, 'name1' AS name, 'type1' AS type, '2022-01-02' AS date,
UNION ALL
SELECT 3 AS id, 'name1' AS name, 'type2' AS type, '2022-01-03' AS date,
UNION ALL
SELECT 4 AS id, 'name1' AS name, 'type1' AS type, '2022-01-03' AS date,
UNION ALL
SELECT 5 AS id, 'name1' AS name, 'type2' AS type, '2022-01-04' AS date,
UNION ALL
SELECT 6 AS id, 'name1' AS name, 'type1' AS type, '2022-01-05' AS date,
UNION ALL
SELECT 7 AS id, 'name2' AS name, 'type1' AS type, '2022-01-03' AS date,
UNION ALL
SELECT 8 AS id, 'name2' AS name, 'type2' AS type, '2022-01-08' AS date,
)
SELECT
t1.id,
t1.name,
t1.type,
t1.date,
t2.id AS type2_id,
FROM (
SELECT *,
FROM logs
WHERE logs.type = 'type1'
) AS t1
LEFT JOIN (
SELECT *,
IFNULL(LAG(logs.date) OVER(PARTITION BY logs.name, logs.type ORDER BY logs.date), '2000-01-1') AS date_prev,
FROM logs
WHERE type = 'type2'
) AS t2
ON t2.name = t1.name
AND t2.date > t1.date
AND t2.date_prev <= t1.date
ORDER BY t1.name, t1.date
Consider below approach
select *, first_value(if(type='type2', id, null) ignore nulls) over win as type2_id
from logs
qualify type = 'type1'
window win as (partition by name order by date rows between 1 following and unbounded following)
if applied to sample data in your question - output is

SQL - how to find unique values from 2 columns?

I'm using SQLite, and would like to get only rows with values that are unique per table. I know how to get values per each column, but can not create a single (sqlite compatible) select to get unique value from both column.
Here is data example:
col_A
col_B
111
777
222
333
222
111
444
333
222
555
333
666
333
777
My desired result from above example would be rows with unique values that include only: 444, 555, 666.
col_A
col_B
444
333
222
555
333
666
222 should not be in results as it appears multiple times in col_A.
777 should not be in results as it appears multiple times in col_B.
I need only single occurrence of value in either col_A or col_B. Unique value per table, not column.
How far i managed to get:
SELECT *
FROM my_table
WHERE
(SELECT col_A as asset from my_table GROUP BY col_A HAVING COUNT(*) = 1
UNION
SELECT col_B as asset from my_table GROUP BY col_B HAVING COUNT(*) = 1)
IN (col_A, col_B)
I have tried with UNION and few other approaches, but can not achieve desired result.
To Start filtering by UNION ALL considering the keyword either
would be suitable option along with HAVING clause to determine the distinct values such as
WITH t1 AS
(
SELECT col
FROM (SELECT col_A AS col FROM t UNION ALL
SELECT col_B FROM t) AS tt
GROUP BY col
HAVING COUNT(*)=1
)
SELECT t.*
FROM t1
JOIN t
ON col IN ( col_A , col_B )
Demo
I think you need a query like this:
;with unqs as (
select col_c
from (
select col_c, count(col_c) cnt
from (
select col_A as col_c
from my_table
union all
select col_B as col_c
from my_table) as t
group by col_c) as tt
where cnt = 1
)
select *
from my_table
where col_A in (select col_c from unqs)
or col_B in (select col_c from unqs);
SQL Fiddle
WITH
symetric AS
(
SELECT col_a, col_b, col_a AS lookup FROM t
UNION ALL
SELECT col_a, col_b, col_b AS lookup FROM t
)
SELECT
col_a, col_b
FROM
symetric AS t
WHERE
NOT EXISTS (
SELECT *
FROM symetric
WHERE lookup = t.lookup
AND (col_a, col_b) <> (t.col_a, t.col_b)
)
ORDER BY
col_a, col_b
Demo ; https://dbfiddle.uk/9tzUASle

Oracle SQL - How to Filter Out Duplicate Row Based on 1 Column

So I've Been trying to only get a unique value for 1 column. For Example I have a Table called "TBL"
COL_A COL_B COL_C
1 "HAT" "RED"
2 "HAT" "BLUE"
3 "SHIRT" "BLUE"
3 "SHOES" "GREEN"
I want to get the table to filter out all the duplicates for COL_A so the end result table would look like this - getting rid of 2 rows that have the duplicate 3 IDs. But still keeping all the columns just only filtering out the duplicates for one column.
COL_A COL_B COL_C
1 "HAT" "RED"
2 "HAT" "BLUE
I've tried multiple ways, first with the DISTINCT selector but after digging there still is a row left with the ID and I have also tried to use the GROUP BY selector and it also comes back with 1 row. If I can create a table that only has the unique values for COL_A then I can join that with my other table to only grab all the other columns but the table will only have the unique values for COL_A.
The statements I've tried were
SELECT DISTINCT COL_A FROM TBL
SELECT COL_A FROM TBL GROUP BY COL_A
I would think that one of these statements would be able to give me my result but instead it returns 3 even though the 3 id has 2 rows. If I could get it to return only 1 and 2 I can join that with another table to get all the other data for the unique IDs
COL_A
1
2
3
Any suggestions?
You can go with A IN clause and a Sub-SELECT
SELECT "COL_A", "COL_B", "COL_C" FROM tabl1
WHERE "COL_B" IN (
SELECT
"COL_B"
FROM tabl1
GROUP BY "COL_B"
HAVING COUNT("COL_B") > 1
AND COUNT(DISTINCT "COL_C") > 1)
ORDER BY "COL_A"
COL_A | COL_B | COL_C
----: | :---- | :----
1 | HAT | RED
2 | HAT | "BLUE
db<>fiddle here
You can use NOT IN to find the ones that are not repeated. For example:
select *
from t
where col_a not in (
select col_a from t group by col_a having count(*) > 1
)
Result:
COL_A COL_B COL_C
------ ------ -----
1 HAT RED
2 HAT BLUE
See example at db<>fiddle.
The WITH clause just generates the sample data and, as such, is not part of the answer.
You can get what you want using analytic function (Count() Over()) as in SQL below...
WITH
tbl AS
(
Select 1 "COL_A", 'HAT' "COL_B", 'RED' "COL_C" From Dual Union All
Select 2 "COL_A", 'HAT' "COL_B", 'BLUE' "COL_C" From Dual Union All
Select 3 "COL_A", 'SHIRT' "COL_B", 'BLUE' "COL_C" From Dual Union All
Select 3 "COL_A", 'SHOES' "COL_B", 'GREEN' "COL_C" From Dual
)
-- ------------------------------------------------------------------------------
SELECT COL_A, COL_B, COL_C
FROM ( Select COL_A, COL_B, COL_C, Count(*) OVER(Partition By COL_A Order By COL_A) "ITERATIONS" From tbl )
WHERE ITERATIONS = 1
More about analytic functions and how to use them at >> https://docs.oracle.com/cd/E11882_01/server.112/e41084/functions004.htm#SQLRF06174 - this is the old one but good. Regards...

Remove Duplicates concatenating columns

I have the data in Table A like this as i/p
Col A | Col B | Col C
PG_1100000357_1100000356 | 1100000357 | 1100000356
PG_1100000356_1100000357 | 1100000356 | 1100000357
PG_10909099_12990909 | 10909099 | 12990909
PG_8989898_79797987 | 8989898 | 79797987
PG_8989898_79797987 | 8989898 | 79797987
I need to write a query to recieve the o/p as -
1) Remove the exact duplicates from the i/p when it matches with another record. (examples 4th & 5th record)
2) We need to consider concatenation of COl B, COl C to concatenation of Col c, Col B and remove that duplicate also. (1st and 2nd record)
note :- COl A is arrived by CONTACT(PG_,Col B,'_',Col c) and dont worry about that
Col A | Col B | Col C
PG_1100000357_1100000356 | 1100000357 | 1100000356
PG_10909099_12990909 | 10909099 | 12990909
PG_8989898_79797987 | 8989898 | 79797987
Could you please help me? Thanks very much in advance.
--Oracle SQL: row_number().
--Least and Greatest functions will work regardless Col_B and Col_C have number or varchar2 data type
with s (Col_A, Col_B, Col_C) as (
select 'PG_1100000357_1100000356', 1100000357, 1100000356 from dual union all
select 'PG_1100000356_1100000357', 1100000356, 1100000357 from dual union all
select 'PG_10909099_12990909' , 10909099 , 12990909 from dual union all
select 'PG_8989898_79797987' , 8989898 , 79797987 from dual union all
select 'PG_8989898_79797987' , 8989898 , 79797987 from dual)
select Col_A, Col_B, Col_C
from
(select s.*,
row_number () over (partition by least(Col_B, Col_C), greatest(Col_B, Col_C) order by Col_B desc) rn
from s
)
where rn = 1;
COL_A COL_B COL_C
------------------------ ---------- ----------
PG_8989898_79797987 8989898 79797987
PG_10909099_12990909 10909099 12990909
PG_1100000357_1100000356 1100000357 1100000356
It's not right to hold the same data in the multiple columns. The values of the Col_B and Col_C are already exist in Col_A, you just need to split them, and then apply group by with least and greatest functions as #akk0rd87 suggested and considering the previous tag oracle :
with Table_A(Col_A) as
(
select 'PG_1100000357_1100000356' from dual union all
select 'PG_1100000356_1100000357' from dual union all
select 'PG_10909099_12990909' from dual union all
select 'PG_8989898_79797987' from dual union all
select 'PG_8989898_79797987' from dual
), t as
(
select regexp_substr(Col_A, '[^_]+', 1, 1) col_one,
regexp_substr(Col_A, '[^_]+', 1, 2) col_two,
regexp_substr(Col_A, '[^_]+', 1, 3) col_three
from Table_A
)
select max(concat(concat(col_one||'-',least(col_two,col_three)||'-'),
greatest(col_two,col_three)))
as Col_A,
least(col_two,col_three) as Col_B, greatest(col_two,col_three) as Col_C
from t
group by least(col_two,col_three), greatest(col_two,col_three);
Demo
The below SQL query returns result as you expected
;WITH CTE AS( SELECT ColA, ColB, ColC,
ROW_NUMBER() OVER (PARTITION BY CASE WHEN ColB > ColC THEN ColB ELSE ColC END ORDER BY ColB) RN
FROM TableA )
SELECT ColA, ColB, ColC FROM CTE WHERE RN =1