Convert columns to rows in SQL [duplicate] - sql

This question already has answers here:
Oracle SQL pivot query
(4 answers)
Closed 4 years ago.
I need to write a query which takes rows and converts it into columns - here's my table:
Count fname lname id
-----------------------------
1 abc def 20
2 pqr 20
3 abc xyz 20
4 xyz xyz 20
1 abc def 21
1 pqr xyz 22
2 abc abc 22
This is the output I'm trying to produce:
id fname lname fname lname fname lname fname lname
-------------------------------------------------------------
20 abc def pqr NULL abc xyz xyz xyz
21 abc def NULL NULL NULL NULL NULL NULL
22 abc abc NULL NULL NULL NULL NULL NULL
The max value of count for each id is 4. I'm using Oracle 9i.

Here's another one you might have some luck with. I like #ThinkJet's but not sure how much decode costs (if more or less than this below.
SELECT
T1.ID,
T1.fname,
T1.lname,
T2.fname,
T2.lname,
T3.fname,
T3.lname,
T4.fname,
T4.lname
FROM
table T1
LEFT JOIN
table T2
ON
T1.ID = T2.ID
AND T2.count = 2
LEFT JOIN
table T3
ON
T1.ID = T3.ID
AND T3.count = 3
LEFT JOIN
table T4
ON
T1.ID = T4.ID
AND T4.count = 4
WHERE
T1.count = 1

Look at this example, same principle as in #Mike M. answer, but with true Oracle realization:
create table my_table (
id number,
fname varchar2(255),
lname varchar2(255),
cnt number
);
insert into my_table(cnt, fname, lname, id) values(1,'abc','def',20);
insert into my_table(cnt, fname, lname, id) values(2,'pqr','' ,20);
insert into my_table(cnt, fname, lname, id) values(3,'abc','xyz',20);
insert into my_table(cnt, fname, lname, id) values(4,'xyz','xyz',20);
insert into my_table(cnt, fname, lname, id) values(1,'abc','def',21);
insert into my_table(cnt, fname, lname, id) values(1,'pqr','xyz',22);
insert into my_table(cnt, fname, lname, id) values(2,'abc','abc',22);
select
tbl.id,
min(decode(tbl.cnt, 1 , fname, null)) fname_1,
min(decode(tbl.cnt, 1 , lname, null)) lname_1,
min(decode(tbl.cnt, 2 , fname, null)) fname_2,
min(decode(tbl.cnt, 2 , lname, null)) lname_2,
min(decode(tbl.cnt, 3 , fname, null)) fname_3,
min(decode(tbl.cnt, 3 , lname, null)) lname_3,
min(decode(tbl.cnt, 4 , fname, null)) fname_4,
min(decode(tbl.cnt, 4 , lname, null)) lname_4
from
my_table tbl
group by
tbl.id
order by
tbl.id
;

I know you're after an Oracle 9i solution, but Oracle 11 introduces PIVOT, which allows you to do queries like:
select *
from mb_test
pivot ( max(fname ) as fname,
max(lname) as lname
for count in (1,2,3,4)
)
order by id
;
which gives:
ID 1_fname 1_lname 2_fname 2_lname 3_fname 3_lname 4_fname 4_lname
20 abc def pqr null abc xyz xyz xyz
21 abc def null null null null null null
22 pqr xyz abc abc null null null null
Not quite what you were after, but extremely useful in many circumstances.... and almost worth the upgrade for PIVOT and UNPIVOT alone
EDIT
Modified to put fname and lname in separate columns

Is this what you're looking for?
http://bytes.com/topic/sql-server/answers/531936-convert-rows-into-columns

Related

How to get next row with id [duplicate]

This question already has answers here:
Is there a way to access the "previous row" value in a SELECT statement?
(9 answers)
Closed 4 months ago.
Lets say I have a table like this;
Id
Name
1
John
2
Doe
5
Rose
11
Michael
15
Pedro
and my select query like this;
Id
Name
1
John
5
Rose
I want to select next rows according to my query which like this;
Id
Name
2
Doe
11
Michael
1 Johns next row is 2 Doe and 5 Roes's next row 11 Michael
One of many ways to do this:
WITH
RowNumbers AS (
SELECT
*,
ROW_NUMBER() OVER (ORDER BY ID) AS RowNumber
FROM
TableName -- replace with your table name
)
SELECT
Id,
Name
FROM
RowNumbers
WHERE
RowNumber IN (
SELECT
RowNumber+1
FROM
RowNumbers
WHERE
Name IN ('John', 'Rose')
)
;
You could use a CTE to create row_numbers and then select and join to it.
create table my_data (
id integer,
name varchar(20)
);
insert into my_data (id, name) values
(1, 'john'),
(2, 'doe'),
(5, 'rose'),
(11, 'michael'),
(15, 'pedro');
with row_data as (
select id, name,
row_number() over (order by id) as rn
from my_data
)
select b.id, b.name
from row_data a
join row_data b
on a.rn + 1 = b.rn
where a.name in ('john','rose')
id
name
2
doe
11
michael
fiddle

Without association select all records from two differnet tables

Without association I want to fetch records from two different table.
Note:
In name column, to identify I want to add table name. (i.e: t1 test1)
There is no relationship between two tables
TABLE A
uid name email
1 test1 a#a.com
2 test2 b#a.com
3 test3 c#a.com
4 test4 d#a.com
TABLE B
pid name email
1 test1 123#a.com
2 test2 456#a.com
3 test3 789#a.com
4 test4 900#a.com
RESULT
uid pid name email
1 null t1-test1 a#a.com
2 null t1-test2 b#a.com
3 null t1-test3 c#a.com
4 null t1-test4 d#a.com
null 1 t2-test1 123#a.com
null 2 t2-test2 456#a.com
null 3 t2-test3 789#a.com
null 4 t2-test4 900#a.com
My try so far:
SELECT distinct table1.id, table1.name, table.email, table2.id, table1.name, table.email
FROM table1, table2
Above query giving repeated records
I think a SQL UNION and the CONCAT() function should help you out here.
SELECT table1.id, CONCAT('t1-',table1.name) as name, table1.email
FROM table1
UNION ALL
SELECT table2.id, CONCAT('t2-',table2.name) as name, table2.email
FROM table1
ORDER BY name
WITH cte AS (
(
SELECT
row_number() OVER (),
NULL,
't1-' || a.name AS name,
a.email
FROM
a)
UNION ALL
SELECT
NULL,
row_number() OVER (),
't2-' || b.name,
b.email
FROM b
)
SELECT
*
FROM
cte
ORDER BY
name;

Hive query optimization

My requirement is to get the id and name of the students having more than 1 email id's and type=1.
I am using a query like
select distinct b.id, b.name, b.email, b.type,a.cnt
from (
select id, count(email) as cnt
from (
select distinct id, email
from table1
) c
group by id
) a
join table1 b on a.id = b.id
where b.type=1
order by b.id
Please let me know is this fine or any simpler version available.
Sample data is like:
id name email type
123 AAA abc#xyz.com 1
123 AAA acd#xyz.com 1
123 AAA ayx#xyz.com 3
345 BBB nch#xyz.com 1
345 BBB nch#xyz.com 1
678 CCC iuy#xyz.com 1
Expected Output:
123 AAA abc#xyz.com 1 2
123 AAA acd#xyz.com 1 2
345 BBB nch#xyz.com 1 1
678 CCC iuy#xyz.com 1 1
you can use group by -> having count() for this requirement.
select distinct b.id
, b.name,
, b.email
, b.type
from table1 b
where id in
(select distinct id from table1 group by email, id having count(email) > 1)
and b.type=1
order by b.id
You can try to use the analytical way of count() function:
SELECT sub.ID, sub.NAME
FROM (SELECT ID, NAME, COUNT (*) OVER (PARTITION BY ID, EMAIL) cnt
FROM raw.crddacia_raw) sub
WHERE sub.cnt > 1 AND sub.TYPE = 1
I strongly recommend using window functions. However, Hive does not support count(distinct) as a window function. There are different methods to solve this. One is the sum of dense_rank()s:
select id, name, email, type, cnt
from (select t1.*,
(dense_rank() over (partition by id order by email) +
dense_rank() over (partition by id order by email desc)
) as cnt
from table1 t1
) t
where type = 1;
I would expect this to have better performance than your version. However, it is worth testing different versions to see which has the better performance (and feel free to come back to let others know which is better).
One more method using collect_set and taking the size of returned array for calculating distinct emails.
Demo:
--your data example
with table1 as ( --use your table instead of this
select stack(6,
123, 'AAA', 'abc#xyz.com', 1,
123, 'AAA', 'acd#xyz.com', 1,
123, 'AAA', 'ayx#xyz.com', 3,
345, 'BBB', 'nch#xyz.com', 1,
345, 'BBB', 'nch#xyz.com', 1,
678, 'CCC', 'iuy#xyz.com', 1
) as (id, name, email, type )
)
--query
select distinct id, name, email, type,
size(collect_set(email) over(partition by id)) cnt
from table1
where type=1
Result:
id name email type cnt
123 AAA abc#xyz.com 1 2
123 AAA acd#xyz.com 1 2
345 BBB nch#xyz.com 1 1
678 CCC iuy#xyz.com 1 1
We still need DISTINCT here because analytic function does not remove duplicates like in case 345 BBB nch#xyz.com.
This is very similar to your query but here i am filtering data at initial step(in inner query)so that the join should not happen on less data
select distinct b.id,b.name,b.email,b.type,intr_table.cnt from table1 orig_table join
(
select a.id,a.type,count(a.email) as cnt from table1 as a where a.type=1 group by a
) intr_table on inter_table.id=orig_table.id,inter_table.type=orig_table.type

SQL View to flatten out a hierarchy

I have a table with some parent child relationships. I want to create a view that has all possible ids for a location id.
I need the sql to do this
Table:
ID PARENT_ID LOCATION_ID
1 NULL ABC
2 1 XYZ
3 NULL EFG
view results:
LOCATION_ID ID
XYZ 1
XYZ 2
ABC 1
ABC 2
EFG 3
You don't mention the database you are using, so I'll assume PostgreSQL. You can adjust the answer to your specific engine:
with recursive
n as (
select id, id as grp, location_id from t where parent_id is null
union all
select t.id, n.grp, t.location_id
from n
join t on t.parent_id = n.id
)
select b.id, a.location_id
from n a
join n b on a.grp = b.grp
Result:
id location_id
-- -----------
1 ABC
2 ABC
1 XYZ
2 XYZ
3 EFG
For the record, the data script I used is:
create table t (
id int,
parent_id int,
location_id varchar(10)
);
insert into t (id, parent_id, location_id) values
(1, null, 'ABC'),
(2, 1, 'XYZ'),
(3, null, 'EFG');

How to find Duplicates in table with more than one column ID , COL1 , COL2 and COL3

How we can find duplicate rows in table with more than one column e.g
table is
Table 1
EMPID FNAME LNAME
1 VIKAS AHLAWAT
1 VIKAS AHLAWAT
2 NIKITA JAIN
3 ASHISH KUMAR
4 NIKHIL SHARMA
5 ANISH KADIAN
6 ANISH KADIAN
and expected result should be as below
EMPID FNAME LNAME
1 VIKAS AHLAWAT
1 VIKAS AHLAWAT
5 ANISH KADIAN
6 ANISH KADIAN
Aggregate based solution will work in all the databases:
select t1.*
from your_table t1
join (
select fname, lname
from your_table
group by fname, lname
having count(*) > 1
) t2 on t1.lname = t2.lname
and t1.fname = t2.fname
If you are using a database which supports window functions, you can use:
select empid, fname, lname
from (
select t.*, count(*) over (partition by fname, lname) cnt
from your_table t
) t
where cnt > 1
If you want just the name who are present multiple times (and not their empids):
select fname, lname
from your_table
group by fname, lname
having count(*) > 1
This query will show you the required results.
select count(empid) cnt,
empid,
fname,
lname
from table_1
group by fname, lanem
having count(empid) > 1
order by 1 desc