How to Pivot Without Aggregation, Is that possible? - sql

I want to rearrange the table with rows and columns in the following order. Is this possible and how?
This is the outcome i expect. How can I do this.

If your have multiple value for each row then what you expect without aggregation function?
And if you have a single value per each row, so it's no matter that use sum,min,max or avg aggregation function.
like this(in oracle):
select row_id, 'C1', 'C2', 'C3'
from table
pivot(max("value") for column_id in('C1', 'C2','C3'));
executable version with sample:
select row_id, 'C1', 'C2', 'C3'
from (select 1 table_id, 'C1' Column_id, 1 Row_id, 20000 "value" from dual union all
select 1 table_id, 'C2' Column_id, 1 Row_id, 30000 "value" from dual union all
select 1 table_id, 'C3' Column_id, 1 Row_id, 25000 "value" from dual union all
select 1 table_id, 'C1' Column_id, 2 Row_id, 80200 "value" from dual union all
select 1 table_id, 'C2' Column_id, 2 Row_id, 50000 "value" from dual union all
select 1 table_id, 'C3' Column_id, 2 Row_id, 95000 "value" from dual)
pivot(max("value") for column_id in('C1', 'C2','C3'))

Select Row_ID,
Min(Case DBColumnName When 'c1' Then Value End) c1,
Min(Case DBColumnName When 'c2' Then Value End) c2,
Min(Case DBColumnName When 'c3' Then Value End) c3
From table
Group By Row_ID
Edit: I have written this without an editor & have not run the SQL. I hope, you get the idea.

Related

SQL query to find products which have different values for a particular id which should be the same

I need a SQL query which will fetch me the list of products which has different values for a same id and product is of table A and Id and values are of table B and both the tables can be joined by column name prod_id
Output I want:
List item
product
abc
So in output I want only the product abc because it has different values for their respective id and I don’t need xyz because it has same values for their respective id
I tried but I’m not getting what I want as mentioned above
select distinct product
from your_table
group by product, id
having count(distinct values) > 1
If I got it right, you have two tables - one with PROD_ID and PRODUCT and the other with PROD_ID, ID, VALS - something like this:
WITH
tbl_a AS
(
Select 1 "PROD_ID", 'abc' "PRODUCT" From DUAL Union All
Select 9 "PROD_ID", 'xyz' "PRODUCT" From DUAL
),
tbl_b AS
(
Select 1 "PROD_ID", '10' "ID", 345678 "VALS" From DUAL Union All
Select 1 "PROD_ID", '10' "ID", 345678 "VALS" From DUAL Union All
Select 1 "PROD_ID", '14' "ID", 458292 "VALS" From DUAL Union All
Select 1 "PROD_ID", '13' "ID", 629351 "VALS" From DUAL Union All
Select 1 "PROD_ID", '13' "ID", 629354 "VALS" From DUAL Union All
Select 9 "PROD_ID", '10' "ID", 375281 "VALS" From DUAL Union All
Select 9 "PROD_ID", '10' "ID", 375281 "VALS" From DUAL Union All
Select 9 "PROD_ID", '12' "ID", 826292 "VALS" From DUAL Union All
Select 9 "PROD_ID", '12' "ID", 826292 "VALS" From DUAL
)
Now, if this is the case, all you have to do is to join the tables by PROD_ID and select the product having more than one distinct value of VALS column grouped by PRODUCT and ID:
Select
a.PRODUCT
From
tbl_a a
Inner Join
tbl_b b ON(b.PROD_ID = a.PROD_ID)
Group By
a.PRODUCT, b.ID
Having
Count(DISTINCT b.VALS) > 1
--
-- Result:
-- PRODUCT
-- -------
-- abc

I need the count of values

I have data in my table in oracle like below
A_CODE B_M P_id
------ ---- ------
123 A 1
123 A 2
123 B 5
678 B 3
678 C 3
678 B 4
123 BC 2
The value "BC" is B and C. The data is not normalized so we need to count it as B and C. I need the counts to be displayed as below per A_CODE
A_CODE B_M COUNT
------- ---- -------
123 A 2
123 B 2
123 C 1
678 B 2
678 C 1
How can i do this in Oracle?
You should use CONNECT BY and CONNECT_BY_ROOT.
I hope this helps:
SELECT A_CODE, B_M, COUNT (*)
FROM ( SELECT A_CODE, SUBSTR (CONNECT_BY_ROOT (B_M), LEVEL, 1) B_M
FROM your_table
CONNECT BY LEVEL <= LENGTH (B_M))
WHERE B_M IS NOT NULL
GROUP BY A_CODE, B_M
ORDER BY A_CODE;
Please try below:
SELECT A_CODE, B_M, COUNT(*) "COUNT" FROM
(SELECT A_CODE, B_M
FROM
(SELECT A_CODE,
SUBSTR(B_M,x.LVL,1) B_M
FROM my_table t,
(SELECT LEVEL LVL FROM dual
CONNECT BY LEVEL <=
(SELECT MAX(LENGTH(B_M)) FROM my_table)
) x
WHERE t.B_M is not null
)
WHERE B_M IS NOT NULL
UNION ALL
SELECT A_CODE, B_M FROM my_table WHERE B_M IS NULL
)
GROUP BY A_CODE,
B_M
ORDER BY A_CODE, B_M;
One option is to join this table to a data set that provides you with the normalised structure you need.
with cte_normaliser as (
select 'A' B_M, 'A' 'B_M_norm from dual union all
select 'B' B_M, 'B' 'B_M_norm from dual union all
select 'C' B_M, 'C' 'B_M_norm from dual union all
select 'BC' B_M, 'B' 'B_M_norm from dual union all
select 'BC' B_M, 'C' 'B_M_norm from dual)
select my_table.A_CODE,
n.B_M_norm,
count(*)
from my_table join
cte_normaliser n on n.B_M = my_table.B_M
group by my_table.A_CODE,
n.B_M_norm;
Using a fixed data set like that might not be feasible if you have a large number of variable code combination, though, and that data set might need to be built dynamically.
Besides having my_table, you create a table with all possible values. That is:
P_VAL
-----
A
B
C
Then, you should be able to obtain the count of occurrences with a join. Something like:
with my_table
as ( select '123' a_code, 'A' b_m, '1' p_id from dual
union select '123' a_code, 'A' b_m, '2' p_id from dual
union select '123' a_code, 'B' b_m, '5' p_id from dual
union select '678' a_code, 'B' b_m, '3' p_id from dual
union select '678' a_code, 'C' b_m, '3' p_id from dual
union select '678' a_code, 'B' b_m, '4' p_id from dual
union select '123' a_code, 'BC' b_m, '2' p_id from dual)
,possible_values
as ( select 'A' p_val from dual
union select 'B' p_val from dual
union select 'C' p_val from dual)
select a_code
,p_val b_m
,count('X') count
from my_table
join possible_values
on instr(b_m,p_val) > 0
group by a_code,p_val
order by a_code,p_val;

How to count consecutive duplicates in a table?

I have below question:
Want to find the consecutive duplicates
SLNO NAME PG
1 A1 NO
2 A2 YES
3 A3 NO
4 A4 YES
6 A5 YES
7 A6 YES
8 A7 YES
9 A8 YES
10 A9 YES
11 A10 NO
12 A11 YES
13 A12 NO
14 A14 NO
We will consider the value of PG column and I need the output as 6 which is the count of maximum consecutive duplicates.
It can be done with Tabibitosan method. Run this, to understand it:
with a as(
select 1 slno, 'A' pg from dual union all
select 2 slno, 'A' pg from dual union all
select 3 slno, 'B' pg from dual union all
select 4 slno, 'A' pg from dual union all
select 5 slno, 'A' pg from dual union all
select 6 slno, 'A' pg from dual
)
select slno, pg, newgrp, sum(newgrp) over (order by slno) grp
from(
select slno,
pg,
case when pg <> nvl(lag(pg) over (order by slno),1) then 1 else 0 end newgrp
from a
);
Newgrp means a new group is found.
Result:
SLNO PG NEWGRP GRP
1 A 1 1
2 A 0 1
3 B 1 2
4 A 1 3
5 A 0 3
6 A 0 3
Now, just use a group by with count, to find the group with maximum number of occurrences:
with a as(
select 1 slno, 'A' pg from dual union all
select 2 slno, 'A' pg from dual union all
select 3 slno, 'B' pg from dual union all
select 4 slno, 'A' pg from dual union all
select 5 slno, 'A' pg from dual union all
select 6 slno, 'A' pg from dual
),
b as(
select slno, pg, newgrp, sum(newgrp) over (order by slno) grp
from(
select slno, pg, case when pg <> nvl(lag(pg) over (order by slno),1) then 1 else 0 end newgrp
from a
)
)
select max(cnt)
from (
select grp, count(*) cnt
from b
group by grp
);
with test as (
select 1 slno,'A1' name ,'NO' pg from dual union all
select 2,'A2','YES' from dual union all
select 3,'A3','NO' from dual union all
select 4,'A4','YES' from dual union all
select 6,'A5','YES' from dual union all
select 7,'A6','YES' from dual union all
select 8,'A7','YES' from dual union all
select 9,'A8','YES' from dual union all
select 10,'A9','YES' from dual union all
select 11,'A10','NO' from dual union all
select 12,'A11','YES' from dual union all
select 13,'A12','NO' from dual union all
select 14,'A14','NO' from dual),
consecutive as (select row_number() over(order by slno) rr, x.*
from test x)
select x.* from Consecutive x
left join Consecutive y on x.rr = y.rr+1 and x.pg = y.pg
where y.rr is not null
order by x.slno
And you can control output with condition in where.
where y.rr is not null query returns duplicates
where y.rr is null query returns "distinct" values.
Just for completeness, here's the actual Tabibitosan method:
with sample_data as (select 1 slno, 'A1' name, 'NO' pg from dual union all
select 2 slno, 'A2' name, 'YES' pg from dual union all
select 3 slno, 'A3' name, 'NO' pg from dual union all
select 4 slno, 'A4' name, 'YES' pg from dual union all
select 6 slno, 'A5' name, 'YES' pg from dual union all
select 7 slno, 'A6' name, 'YES' pg from dual union all
select 8 slno, 'A7' name, 'YES' pg from dual union all
select 9 slno, 'A8' name, 'YES' pg from dual union all
select 10 slno, 'A9' name, 'YES' pg from dual union all
select 11 slno, 'A10' name, 'NO' pg from dual union all
select 12 slno, 'A11' name, 'YES' pg from dual union all
select 13 slno, 'A12' name, 'NO' pg from dual union all
select 14 slno, 'A14' name, 'NO' pg from dual)
-- end of mimicking a table called "sample_data" containing your data; see SQL below:
select max(cnt) max_pg_in_queue
from (select count(*) cnt
from (select slno,
name,
pg,
row_number() over (order by slno)
- row_number() over (partition by pg
order by slno) grp
from sample_data)
where pg = 'YES'
group by grp);
MAX_PG_IN_QUEUE
---------------
6
SELECT MAX(consecutives) -- Block 1
FROM (
SELECT t1.pg, t1.slno, COUNT(*) AS consecutives -- Block 2
FROM test t1 INNER JOIN test t2 ON t1.pg = t2.pg
WHERE t1.slno <= t2.slno
AND NOT EXISTS (
SELECT * -- Block 3
FROM test t3
WHERE t3.slno > t1.slno
AND t3.slno < t2.slno
AND t3.pg != t1.pg
)
GROUP BY t1.pg, t1.slno
);
The query calculates the result in following way:
Extract all couples of records that don't have a record with different value of PG in between (blocks 2 and 3)
Group them by PG value and starting SLNO value -> this counts the consecutive values for any [PG, (starting) SLNO] couple (block 2);
Extract Maximum value from query 2 (block 1)
Note that the query may be simplified if the slno field in table contains consecutive values, but this seems not your case (in your example record with SLNO = 5 is missing)
Only requiring a single aggregation query and no joins (the rest of the calculation can be done with ROW_NUMBER, LAG and LAST_VALUE):
SELECT MAX( num_before_in_queue ) AS max_sequential_in_queue
FROM (
SELECT rn - LAST_VALUE( has_changed ) IGNORE NULL OVER ( ORDER BY ROWNUM ) + 1
AS num_before_in_queue
FROM (
SELECT pg,
ROW_NUMBER() OVER ( ORDER BY slno ) AS rn,
CASE pg WHEN LAG( pg ) OVER ( ORDER BY slno )
THEN NULL
ELSE ROW_NUMBER() OVER ( ORDER BY sl_no )
END AS change
FROM table_name
)
WHERE pg = 'Y'
);
Try to use row_number()
select
SLNO,
Name,
PG,
row_number() over (partition by PG order by PG) as 'Consecutive'
from
<table>
order by
SLNO,
NAME,
PG
This is should work with minor tweaking.
--EDIT--
Sorry, partiton by PG.
The partitioning tells the row_number when to start a new sequence.

Oracle SQL query using case when, compacting null fields

I have a table like this:
Items
id group old_new object
1 A O pen
2 A N house
3 B O dog
4 B O cat
5 C N mars
6 C O sun
7 C N moon
8 C o earth
I would like the select return:
Items
group new_object old_object
A house pen
B null dog
B null cat
C mars sun
C moon earth
If I try:
select id,
case when old_new = 'N' then object end as new_object,
case when old_new = 'O' then object end as old_object
from the_table
order by id;
I have 8 row with many field as null
es: last rows:
group new_object old_object
C mars null
c null sun
C moon null
c null earth
But of group C I want only 2 rows...
is not like the other query 'Oracle sql join same table ecc...' because here don't want null result
I'm going to make the assumption that Old and New records are paired in the order they appear based on the ID value. With that assumption the following query:
WITH DTA(ID, GRP, OLD_NEW, OBJECT) AS (
select 1, 'A', 'O', 'pen' from dual union all
select 2, 'A', 'N', 'house' from dual union all
select 3, 'B', 'O', 'dog' from dual union all
select 4, 'B', 'O', 'cat' from dual union all
select 5, 'C', 'N', 'mars' from dual union all
select 6, 'C', 'O', 'sun' from dual union all
select 7, 'C', 'N', 'moon' from dual union all
select 8, 'C', 'O', 'earth' from dual
), dta2 as (
select dta.*
, row_number() over (partition by GRP, old_new order by id) rn
from dta
)
select coalesce(n.grp, o.grp) grp
, n.object new_object
, o.object old_object
from (select * from dta2 where old_new = 'N') n
full join (select * from dta2 where old_new = 'O') o
on n.grp = o.grp
and n.rn = o.rn;
Aside from the sample data section (with dta) this script first uses the analytic function ROW_NUMBER() to add a sequential number partitioned by the group and old_new columns. It then performs a full outer join on two inline views of the dta2 subfactored query, one for thr old objects and one for the new objects. The result, at least for this data set is:
GRP NEW_OBJECT OLD_OBJECT
--- ---------- ----------
A house pen
B dog
B cat
C mars sun
C moon earth
In the first step assign an index (IDX) of the chnage withing your group. I'm using order by ID, but this is upon you. The important thing is that the old and new valuea are unique connected with GRP and IDX.
In next step let PIVOT work for you (I'm using the data from #Sentinel, thx!)
WITH DTA(ID, GRP, OLD_NEW, OBJECT) AS (
select 1, 'A', 'O', 'pen' from dual union all
select 2, 'A', 'N', 'house' from dual union all
select 3, 'B', 'O', 'dog' from dual union all
select 4, 'B', 'O', 'cat' from dual union all
select 5, 'C', 'N', 'mars' from dual union all
select 6, 'C', 'O', 'sun' from dual union all
select 7, 'C', 'N', 'moon' from dual union all
select 8, 'C', 'O', 'earth' from dual
), DTA2 as (
SELECT
ROW_NUMBER() OVER (PARTITION BY GRP,OLD_NEW order by ID) as IDX,
GRP, OLD_NEW, OBJECT
from DTA
)
select * from DTA2
PIVOT (max(OBJECT) OBJECT for (OLD_NEW) in
('N' as "NEW",
'O' as "OLD"
))
order by GRP;
result
IDX, GRP, NEW_OBJECT, OLD_OBJECT
1 A house pen
1 B dog
2 B cat
2 C moon earth
1 C mars sun
Here's an alternative using PIVOT to get the results:
with items as (select 1 id, 'A' grp, 'O' old_new, 'pen' obj from dual union all
select 2 id, 'A' grp, 'N' old_new, 'house' obj from dual union all
select 3 id, 'B' grp, 'O' old_new, 'dog' obj from dual union all
select 4 id, 'B' grp, 'O' old_new, 'cat' obj from dual union all
select 5 id, 'C' grp, 'N' old_new, 'mars' obj from dual union all
select 6 id, 'C' grp, 'O' old_new, 'sun' obj from dual union all
select 7 id, 'C' grp, 'N' old_new, 'moon' obj from dual union all
select 8 id, 'C' grp, 'O' old_new, 'earth' obj from dual)
-- end of mimicking your items table with data in it. See SQL below:
select grp,
new_object,
old_object
from (select grp,
old_new,
obj,
row_number() over (partition by grp, old_new order by id) rn
from items)
pivot (max(obj)
for old_new in ('N' new_object,
'O' old_object))
order by grp,
rn;
GRP NEW_OBJECT OLD_OBJECT
--- ---------- ----------
A house pen
B dog
B cat
C mars sun
C moon earth
Provided that
there's at most one new object for each old,
there's no new object without old object, and
there's at most one old object for any group (this is not true for your sample data, but in comments you indicate you're interested in such solution as well)
a simpler query may be used than for the general case:
select
old.group as group, new.object as new_object, old.object as old_object
from
(select group, object from my_table where old_new = 'O') old
left join
(select group, object from my_table where old_new = 'N') new
on (old.group = new.group)

Find count value for a different group with reference with single column (Oracle)

I have the below scenario data. I need a count for column 'c1' with different set of data. total count should be based on unique no of data from column c1 and e1.
with t as
(
select 'cab1' as c1, 'ae1' as e1 from dual
union all
select 'cab1' , 'ae2' from dual
union all
select 'cab1' , 'ae3' from dual
union all
select 'cab1' , 'ae4' from dual
union all
select 'cab3' , 'ae1' from dual
union all
select 'cab3' , 'ae1' from dual
union all
select 'cab2' , 'ae' from dual
)
select
c1,e1, COUNT(*) OVER (partition by c1 order by c1,e1 ) as p1
from t;
my result should be
c1 e1 count
-----------------------
cab1 ae3 4
cab1 ae2 4
cab1 ae1 4
cab1 ae4 4
cab2 ae 1
cab3 ae1 1
Can anyone help on this.
SqlFiddleDemo
with t as
(
select 'cab1' as c1, 'ae1' as e1 from dual
union all
select 'cab1' , 'ae2' from dual
union all
select 'cab1' , 'ae3' from dual
union all
select 'cab1' , 'ae4' from dual
union all
select 'cab3' , 'ae1' from dual
union all
select 'cab3' , 'ae1' from dual
union all
select 'cab2' , 'ae' from dual
)
SELECT
c1,
e1,
COUNT(*) OVER (partition by c1) as p1
FROM t
GROUP BY c1, e1