oracle transposing rows to columns - sql

my question is about transposing rows into columns.
I have got table T1(c1,c2,c3,c4,c5) columns with datatype varchar2, i want to transpose the rows obtained,
example:
select * from T1
gives
c1 c2 c3 c4 c5
row1 1 2 3 4 5
row2 A B C D E
....
rown U V W X Y
the result expected is
C1 1 A......U
C2 2 B......V
C3 3 C......W
C4 4 D......X
C5 5 E......Y
all rows in different columns(table contains only 10-15 rows)
i have tried the following query, but it isnt giving expected result.
Select RN,value
From (
Select x.*,row_number ()
Over ( Order By c1) rn From T1 x)
Unpivot (value For value_type In (C1,c2,c3,c4,c5)
);

So you only need to pivot data again:
dbfiddle demo
select *
from (
select rn, val, col
from (select t1.*, row_number() over (order by c1) rn from t1)
unpivot (val for col in (c1, c2, c3, c4, c5)))
pivot (max(val) for rn in (1, 2, 3, 4))
order by col
You have to know how many rows are in t1 and list them all in pivot in clause (1, 2, 3, 4) alternatively adding aliases for each column.

Related

postgresql - count distinct combination of three columns- order doesn't matter

I'm trying to count distinct combinations of three columns, order of the columns doesn't matter
sample :
a a a
a a b
a b a
b b a
b a b
the result I'm getting :
a a a 1
a a b 1
a b a 1
b b a 1
b a b 1
desired result
aaa 1
aab 2
bba 2
You can use an ordered array
select v[1], v[2], v[3], count(*) n
from tbl t
cross join lateral (
select array_agg(col order by col) v
from (
values (c1),(c2),(c3)
) t(col)
) s
group by v[1], v[2], v[3];
db<>fiddle
Maybe you can use checksums for getting the required result eg if it is really just combinations 'a' and 'b' that you are dealing with, you could convert the letters to integers (by calling the ASCII() function) and add these up so that you get a checksum.
TABLE
create table t (c1, c2, c3 ) as
select 'a', 'a', 'a' union all
select 'a', 'a', 'b' union all
select 'a', 'b', 'a' union all
select 'b', 'b', 'a' union all
select 'b', 'a', 'b' ;
Checksums
select c1, c2, c3, ascii( c1 ) + ascii( c2 ) + ascii( c3 ) as checksum
from t ;
-- output
c1 c2 c3 checksum
a a a 291
a a b 292
a b a 292
b b a 293
b a b 293
If this works for you, then you can use window functions eg
select c1, c2, c3, rc_ as rowcount
from (
select c1, c2, c3
, count(*) over ( partition by ascii( c1 ) + ascii( c2 ) + ascii( c3 ) order by 1 ) rc_
, row_number() over ( partition by ascii( c1 ) + ascii( c2 ) + ascii( c3 ) order by 1 ) rn_
from t
) sq
where rc_ = rn_ ;
-- output
c1 c2 c3 rowcount
a a a 1
a b a 2
b a b 2
See dbfiddle.
If you are dealing with strings that cannot easily converted to integers, you could create a mapping between the strings and integers, and implement the map_ as a view (so that it is easy to use in subsequent queries) eg
MAP
-- {1} find all distinct elements
-- {2} map each element to an integer
create view map_
as
select val_, rank() over ( order by val_ ) weight_
from (
select distinct val_
from (
select distinct c1 val_ from t union all
select distinct c2 from t union all
select distinct c3 from t
) all_elements
) unique_elements ;
Once you have this map, you can use its values for creating checksums (maybe also in a view) ...
Checksums
create view t_checksums_
as
select c1, c2, c3, c1weight + c2weight + c3weight as checksum
from (
select
c1, ( select weight_ from map_ where c1 = map_.val_ ) c1weight
, c2, ( select weight_ from map_ where c2 = map_.val_ ) c2weight
, c3, ( select weight_ from map_ where c3 = map_.val_ ) c3weight
from t
) valandweight ;
... and then, you can use the same query as before, for obtaining the final result - see dbfiddle.

Repeating rows based on the frequency

I have a table with 2 columns named A and B which is defined as :
A B
c1 2
c2 3
c3 4
The expected output is :
A B
c1 1
c1 2
c2 1
c2 2
c2 3
c3 1
c3 2
c3 3
c3 4
CREATE TABLE #table2
([A] varchar(2), [B] int)
;
INSERT INTO #table2
([A], [B])
VALUES
('c1', 2),
('c2', 3),
('c3', 4)
;WITH nums AS
(SELECT 1 AS value ,a,b from #table2
UNION ALL
SELECT value + 1 AS value ,A,b
FROM nums
WHERE nums.value <B)
SELECT a,value
FROM nums order by a,value
output
a value
c1 1
c1 2
c2 1
c2 2
c2 3
c3 1
c3 2
c3 3
c3 4
Created a Table valued function in which i used Recursive cte to evaluate recurring value of given in put Then Join the table with function using Cross Apply
CREATE FUNCTION [dbo].[udf_GetData] (
#Data INT
)
RETURNS #output TABLE (
Data INT
)
BEGIN
;WITH CTe
AS
(
SELECT 1 As Data
UNION ALL
SELECT Data+1
FROM CTe
WHERE Data < #Data
)
INSERT INTO #output
SELECT Data FROM CTe
RETURN
END
Sample Data and explained How to call function using CROSS APPLY
DECLARE #Data AS TABLE (A VARCHAR(10),B INT)
INSERT INTO #Data
SELECT 'c1', 2 UNION ALL
SELECT 'c2', 3 UNION ALL
SELECT 'c3', 4
SELECT d.A,
(SELECT [dbo].[udf_GetData](d.B)) AS RecData
FROM #Data d
Result
A RecursiveData
----------------
c1 1
c1 2
c2 1
c2 2
c2 3
c3 1
c3 2
c3 3
c3 4
You could try this:
// test data
declare #tbl table(A char(2), B int);
insert into #tbl values
('c1', 2),
('c2', 3),
('c3', 4);
// create CTE with numbers which we will need to join
declare #max int;
select #max = max(B) from #tbl;
;with numbers as (
select 1 n
union all
select n + 1 from numbers
where n < #max
)
// join numbers with your table
select A, n from #tbl t
join numbers n on t.B >= n.n
order by A, n
Say, your table name is test.
WITH r(a, b, repeat) as
(SELECT a, b, 1 from test
union all
select a, b, repeat+1 from r
where r.repeat < r.b)
select * from r
ORDER BY a, repeat;

SQL - find rows that have the same set of values in a column

I have a table and I want to select all rows that have exactly the same set of values that appear in a column and return them as a pair of a certain column.
For example, let's say I have a table named Table:
C1 C2
1 1
1 2
1 3
2 1
3 1
3 2
3 3
4 1
4 2
When I run my query, it should return the row:
1 3
because these are the two values in C1 that have the same set of values in column C2 (1,2,3).
I have an incorrect query below that returns all rows that have at least one matching value in C2 and I can't figure out how to correct it.
SELECT DISTINCT T1.C1, T2.C1
FROM Table T1, Table T2
WHERE T1.C1 != T2.C1
AND T1.C2 = T2.C2
AND T1.C1 < T2.C1
GROUP BY s1.suppId, s2.suppId;
Any help would be greatly appreciated, thanks.
As you do not specify, this one will handle the case of two sets of matching C2 values, outputting two rows - one for each matched set.
with thetable as (
SELECT 1 C1, 1 c2 from dual union
SELECT 1 C1, 2 c2 from dual union
SELECT 1 C1, 3 c2 from dual union
SELECT 2 C1, 1 c2 from dual union
SELECT 3 C1, 1 c2 from dual union
SELECT 3 C1, 2 c2 from dual union
SELECT 3 C1, 3 c2 from dual union
SELECT 4 C1, 1 c2 from dual union
SELECT 4 C1, 2 c2 from dual union
-- added fourrows to give a second set of matches
SELECT 5 C1, 1 c2 from dual union
SELECT 5 C1, 2 c2 from dual union
SELECT 6 C1, 1 c2 from dual union
SELECT 6 C1, 2 c2 from dual )
SELECT LIST_C2, List_c1
FROM (
SELECT list_c2
,LISTAGG(c1,',') WITHIN GROUP (ORDER BY c1) list_c1
FROM (
SELECT c1, LISTAGG(c2,',') WITHIN GROUP (ORDER BY c2) list_c2
FROM thetable
GROUP BY c1
)
group by list_c2
)
-- only bring back where we had more than one c1
WHERE instr(list_c1,',') != 0
Showing the following two sets of C2 values and their matching lists of C1s
LIST_C2 LIST_C1
"1,2" "4,5,6"
"1,2,3" "1,3"
listagg is the rescue (instead of count as commented by #Juan)
with lst as (
select c1, LISTAGG(c2, '; ') WITHIN GROUP (ORDER BY c2) c2_lst
from tst
group by c1
)
select lst1.c1 c1a, lst2.c1 c1b
from lst lst1
inner join lst lst2
on lst1.c2_lst = lst2.c2_lst and
lst1.c1 < lst2.c1
;
gives as requested
1 3
SQLFiddleDemo
Also be prepared, this works only small data (the concatenated key is limited with 4000 bytes).
UPDATE
listagg concatenates all values in the group. To get only distinct values (i.e. set of values) an additional query with DISTINCT must be performed.
WITH lst AS
( SELECT DISTINCT c1,c2 FROM tst
),
lst_dist AS
(SELECT c1,
LISTAGG(c2, '; ') WITHIN GROUP (
ORDER BY c2) c2_lst
FROM lst
GROUP BY c1
)
SELECT lst1.c1 c1a,
lst2.c1 c1b
FROM lst_dist lst1
INNER JOIN lst_dist lst2
ON lst1.c2_lst = lst2.c2_lst
AND lst1.c1 < lst2.c1
You can do this without list agg, using a self join:
select t1.c1, t2.c1
from (select t.*, count(*) over (partition by c1) as cnt
from table t
) t1 join
(select t.*, count(*) over (partition by c1) as cnt
from table t
) t2
on t1.c2 = t2.c2 and t1.c1 < t1.c2 and t1.cnt = t2.cnt
group by t1.c1, t2.c1
having count(*) = max(t1.cnt);
Note: this assumes that there are no duplicate rows in the table. A slight variation can work in that case as well.
This joins the rows on the second column and then aggregates by the first. Along the way, it makes sure that the number of matching columns is the same in the two table and that all columns match.

How to add rows to second table from the first table using Common table expression in SQL Server

For example, I have the first table and I want to fill the second table using the first table.
Table A
Name A B C D
-----------------------
name1 a1 b1 c1 d1
name2 a2 b2 c2 d2
Table B (Desired Format)
ID Name Code
----------------------
1 name1 a1
2 name1 b1
3 name1 c1
4 name1 d1
5 name2 a2
6 name2 b2
7 name2 c2
8 name2 d2
As per the suggestions of #Surendra Nath GM, I tried this
DECLARE #counter as int
SET #counter = 1;
;WITH Actual AS
(
SELECT ROW_NUMBER() OVER( ORDER BY IDKEY) as ID FROM Northwind.dbo.Table1
WHERE
),FIRST AS
(
SELECT ((ROW_NUMBER() OVER( ORDER BY IDKEY))*4-3) AS ID,Name, A
FROM Northwind.dbo.Table1
),SECOND AS
(
SELECT ((ROW_NUMBER() OVER( ORDER BY IDKEY))*4-2) AS ID, Name ,B
from Northwind.dbo.Table1
), NEXT AS
(
SELECT ((ROW_NUMBER() OVER( ORDER BY IDKEY))*4-1) AS ID, Name, C
from Northwind.dbo.Table1next
), ________ as
(
SELECT ((ROW_NUMBER() OVER( ORDER BY IDKEY))*4) AS ID, Name, D
from Northwind.dbo.Table1
)
#counter = #counter+1;
)
SELECT * FROM FIRST
UNION ALL
SELECT * FROM SECOND
UNION ALL
SELECT * FROM NEXT
UNION ALL
SELECT * FROM _________
ORDER BY ID
Here, what do I use after the "SECOND AS". I tried writing "THIRD AS" but apparently no such command exists so after some searching, I could write the third row using "NEXT AS" but I am completely clueless as to how do I insert the Fourth row.
I know I can simply write 4 insert commands for each of A,B,C and D but then I will get the not codes in the following order which is NOT desired:
Table B(NOT DESIRED IN THIS FORMAT)
ID Name Code
----------------------
1 name1 a1
2 name2 a2
3 name1 b1
4 name2 b2
5 name1 c1
6 name2 c2
7 name1 d1
8 name2 d2
Also, the desired format can be achieved using simple loops but in the project I am working on, there are around 200000 rows and the simple loops method takes a lot of time. So, I want to do it using CTE. Please help.
You can do it with two inserts:
insert into tableB(A, B)
select A1, B1
from tableA;
insert into tableB(A, B)
select A2, B2
from tableA;
Or union the table with one insert:
insert into tableB(A, B)
select A1, B1
from tableA union all
select A2, B2
from tableA;
This assumes that id is defined as id int indentity(1, 1) so it automatically increments.
If not, you could assign it as:
insert into tableB(id, A, B)
select row_number() over (order by (select NULL)) as id, A1, B1
from (select A1, B1
from tableA union all
select A2, B2
from tableA
) t
getting the ID will be the tricky part over here, in order to do that and use the set opertaions, you have to split the job into two and generate odd ID's for the select of A1,B1 and generate event ID's for the select of A2,B2
Shown as below
;WITH Actual AS
(
SELECT ROW_NUMBER() OVER( ORDER BY Name) as RN, * FROM Table1
),FIRST AS
(
SELECT ((RN*2)-1) AS ID,A1,B1
FROM TABLE1
ORDER BY RN
),SECOND AS
(
SELECT (RN*2) AS ID,A2,B2
FROM TABLE1
ORDER BY RN
)
SELECT * FROM FIRST
UNION ALL
SELECT * FROM SECOND
if you have four columns then you can use the below query instead
;WITH Actual AS
(
SELECT ROW_NUMBER() OVER( ORDER BY Name) as RN, * FROM Table1
),FIRST AS
(
SELECT ((RN*2)-1) AS ID,A1,B1,C1,D1
FROM TABLE1
ORDER BY RN
),SECOND AS
(
SELECT (RN*2) AS ID,A2,B2,C2,D2
FROM TABLE1
ORDER BY RN
)
SELECT * FROM FIRST
UNION ALL
SELECT * FROM SECOND

Select only rows that have unique fields

What is an SQL command that checks for rows that have rows with no duplicate fields in them.
ex:
A A A B B B should not be in the resulting table.
Only rows such as A B C D E F
i.e. given data like:
A A A B B B
A B C D E F
A A B G H Q
Should return A B C D E F
There is no simple command to do this.
is seems an unusual requirement and possibly an indication that the table is not in first normal form if all columns are interchangeable.
The following works in Microsoft SQL Server
;With YourData AS
(
select 'A' as C1, 'A' as C2, 'A' as C3, 'B' as C4, 'B' as C5, 'B' as C6 UNION ALL
select 'A' as C1, 'B' as C2, 'C' as C3, 'D' as C4, 'E' as C5, 'F' as C6
)
SELECT *
FROM YourData
WHERE 1 =
( SELECT TOP 1 COUNT(*) AS Cnt
FROM (
SELECT C1 AS C
UNION ALL
SELECT C2
UNION ALL
SELECT C3
UNION ALL
SELECT C4
UNION ALL
SELECT C5
UNION ALL
SELECT C6
) D
GROUP BY C
ORDER BY Cnt DESC
)
Select distinc * returns unique ROWS not unique values from fields.
You should compare each column's value with others. (Assuming column types are the same). For example, for a 4 column table you should do smoething like:
SELECT Col1, Col2, Col3, Col4 FROM MyTable WHERE
Col1 NOT IN (Col2,Col3,Col4) AND
Col2 NOT IN (Col3,Col4) AND
Col3 <> Col4
SELECT DISTINCT * FROM tablename
SELECT DISTINCT col FROM tabl
SELECT * FROM
mytable
WHERE mytable.col1 != mytable.col2 != mytable.col3 ...