Convert rows into columns using pivot in oracle 11g - sql

i want to convert rows into columns using PIVOT function.
Example is below
Table
EMP No EMP Name
------- --------
1 ABC
2 PQR
Output should be
Col1 Col2
---- ----
Emp No 1
Emp Name ABC
EMP No 2
Emp Name PQR
I am ok with loop and all however we should have used PIVOT, have serached google however has not got anything matching.
Please suggest and send some sample code.

Actually for you requirement, you need unpivot, not pivot. But for that, datatype of both columns should be same, character in this case
with tab(emp_no, emp_name) as (
select '1' ,'abc' from dual union all
select '2' ,'PQR' from dual)
----
--End of Data Perparation
----
select *
from tab
unpivot (col2 for col1 in ( emp_no as 'EMP No', emp_name as 'Emp Name'));
Output
| COL1 | COL2 |
|----------|------|
| EMP No | 1 |
| Emp Name | abc |
| EMP No | 2 |
| Emp Name | PQR |

I'm not sure if this can be done using PIVOT. You can select the columns separately and UNION them.
select 'Emp No' col1, emp_no col2 from tab
union all
select 'Emp Name', emp_name from tab;
Note that, both the columns should be of same datatype. Else you need to cast/convert one of them.
Also, the result may not be in the order you want. You need to sepecify the order by clause explicitly.
select col1, col2 from(
select 'Emp No' col1, emp_no col2, emp_no from tab
union all
select 'Emp Name', emp_name, emp_no from tab
)
order by emp_no, case col1 when 'Emp No' then 1 else 2 end;
sqlfidle.

Related

SQL analytic ORACLE Converting rows into columns

I have a table structue which has 2 columns as mentioned below
Column_name old Value New_Value
:--------- :------- :---------
Name NULL John
Age NULL 45
Status Active Inactive
I need a output like
John Name
45 Age
Inactive Status
Could you please help me out here.
Looks like a NVL to me ... Sample data in lines #1 - 5; query that does it begins at line #6.
SQL> with test (column_name, old_value, new_value) as
2 (select 'Name' , null , 'John' from dual union all
3 select 'Age' , null , '45' from dual union all
4 select 'Status', 'Active', 'Inactive' from dual
5 )
6 select nvl(new_value, old_value) value,
7 column_name
8 from test;
VALUE COLUMN
-------- ------
John Name
45 Age
Inactive Status
SQL>

How to add several unpivot() functions in the same select statement ORACLE

Here is my example,
WITH TABLE1 (ID,COL1,COL2,SUBCOL1,SUBCOL2)
as (select 1, 'm1',null,'s1',null from dual
union all
select 2, null ,'m2', null,'s2' from dual
)
select * from TABLE1;
From above table1 I want to create a view as follows,
ID | COLTYPE | col | SUBCOLTYPE | subcol
-------------------------------------------------------------
1 COL1 m1 SUBCOL1 s1
2 COL2 m2 SUBCOL2 s2
what I did was I merged COL1 , COL2 in to COL and SUBCOL1, SUBCOL2 in to SUBCOL. Can I achieve this by using UNPIVOT() function.
my imaginary query is as follows,
select * from table1
unpivot(COL for COLTYPE in (COL1,COL2)) --- FIRST MERGE
unpivot(SUBCOL FOR SUBCOLTYPE IN (SUBCOL1,SUBCOL2)) ---SECOND MERGE
;
Each first and second merge lines are working individually when comment other one. But They are not working same time. How to add several unpivot() function in the same select statement. Is it possible to do ?
Answer to the original question
Why do you want to use unpivot? I ask because you are not transposing columns into rows. Is it guaranteed that all the columns you want to merge are NULL except one of them? If yes you could use the coalesce function.
Source data:
ID COL1 COL2 SUBCOL1 SUBCOL2
---------- -------- -------- -------- --------
1 m1 s1
2 m2 s2
Example:
WITH TABLE1 (ID,COL1,COL2,SUBCOL1,SUBCOL2)
as (select 1, 'm1',null,'s1',null from dual
union all
select 2, null ,'m2', null,'s2' from dual
)
select id,
coalesce(col1,col2) as col,
coalesce(subcol1, subcol2) as subcol
from TABLE1;
Result:
ID COL SUBCOL
---------- -------- --------
1 m1 s1
2 m2 s2
Answer to the updated question
After you edited your question, this syntax is probably what you are looking for:
WITH TABLE1 (ID,COL1,COL2,SUBCOL1,SUBCOL2)
as (select 1, 'm1',null,'s1',null from dual
union all
select 2, null ,'m2', null,'s2' from dual
)
select id, coltype, col, subcoltype, subcol from TABLE1
UNPIVOT ((col, subcol) FOR (coltype, subcoltype) IN ((col1, subcol1) AS ('col1', 'subcol1'), (col2, subcol2) AS ('col2', 'subcol2')));
Result:
ID COLTYPE COL SUBCOLTYPE SUBCOL
---------- ---------------- -------- ---------------------------- --------
1 col1 m1 subcol1 s1
2 col2 m2 subcol2 s2

How to make a query on multiple columns without writting all possible permutations?

I'm at my first question on stackexchange, as i have a few days since I struggle on this matter:
I want to make a complex query(PLSQL) on a table that has col1,col2,col3,col4,col5 having values like (names: which are split- one part per column)
+------+--------+--------+--------+------+
| ID | Col1 | Col2 | Col3 | Col4 |
+------+--------+--------+--------+------+
| (#1) | Andrew | Joan | Bach | Mike |
| (#2) | Mark | Andrew | Livy | |
| (#3) | Joan | Arch | Donnie | |
| (#4) | Joan | Andrew | Lyx | |
+------+--------+--------+--------+------+
Number of name parts differ from 1 to 5.
I want to search in different combinations like this:
search for Bach Joan Mike - get #1, #3,#4 - in this order
search for Andrew Bach - get the following order #1,#2,#4 - in this order
I don't like the idea of using a looooong query in which I will write all possible permutations in order to mach each part of my search string
What I'd like to achieve is this:
first set: match all those n parts (both Bach and Joan and Bach match, in any order)
second set: match n-1 parts (at least N-1 of my search parts match the row, in any order)
third set: match n-2 parts
I use a ORACLE database and I was thinking on making it in a stored procedure: match_my_set(query_str,col1,col2,col3,col4,col5). I would write at least 5 loops (loop into loop) in order to achieve this, but I doubt it is a professional idea . .
Any help is appreciated. Thank you
If you're on 11g or higher, you can unpivot your columns into rows; this is using a CTE to provide your sample data:
with t (id, col1, col2, col3, col4, col5) as (
select 1, 'Andrew', 'Joan', 'Bach', 'Mike', null from dual
union all select 2, 'Mark', 'Andrew', 'Livy', null, null from dual
union all select 3, 'Joan', 'Arch', 'Donnie', null, null from dual
union all select 4, 'Joan', 'Andrew', 'Lyx' , null, null from dual
)
select * from t
unpivot (name for col_no in (col1 as 1, col2 as 2, col3 as 3, col4 as 4, col5 as 5));
ID COL_NO NAME
---------- ---------- ------
1 1 Andrew
1 2 Joan
1 3 Bach
1 4 Mike
2 1 Mark
2 2 Andrew
2 3 Livy
...
Then you can look for matches against the single name column:
select distinct id
from (
select * from t
unpivot (name for col_no in (col1 as 1, col2 as 2, col3 as 3, col4 as 4, col5 as 5))
)
where name in ('Bach', 'Joan', 'Mike')
order by id;
ID
----------
1
3
4
I think you want to make the ordering more complicated though, by counting how many of the terms match in each row. If so you can do:
select id, count(*) as cnt
from (
select * from t
unpivot (name for col_no in (col1 as 1, col2 as 2, col3 as 3, col4 as 4, col5 as 5))
)
where name in ('Bach', 'Joan', 'Mike')
group by id;
ID CNT
---------- ----------
1 3
4 1
3 1
and then have another level of inline view to order by the count, with some way to break ties:
select id
from (
select id, count(*) as cnt
from (
select * from t
unpivot (name for col_no in (col1 as 1, col2 as 2, col3 as 3, col4 as 4, col5 as 5))
)
where name in ('Bach', 'Joan', 'Mike')
group by id
)
order by cnt desc, id;
Which gets the same result with your sample data. Changing the IN condition to user ('Andrew', 'Bach') also gets 1,2,4 with both versions.
Depending on how you're getting the values you're searching for, you might want to use an array instead (via a table collection expression and a join), or tokenise a string containing all the search words, or some other variation.
You can do it using Oracle's collections (which should work in 10g or later)
Oracle Setup:
CREATE TABLE TABLE_NAME( ID, Col1, Col2, Col3, Col4 ) AS
SELECT 1, 'Andrew', 'Joan', 'Bach', 'Mike' FROM DUAL UNION ALL
SELECT 2, 'Mark', 'Andrew', 'Livy', NULL FROM DUAL UNION ALL
SELECT 3, 'Joan', 'Arch', 'Donnie', NULL FROM DUAL UNION ALL
SELECT 4, 'Joan', 'Andrew', 'Lyx', NULL FROM DUAL;
CREATE TYPE stringlist AS TABLE OF VARCHAR2(100);
/
Query:
SELECT id,
col1,
col2,
col3,
col4
FROM (
SELECT t.*,
stringlist( col1, col2, col3, col4 )
MULTISET INTERSECT
stringlist( 'Bach', 'Joan', 'Mike' ) -- Search terms
AS names
FROM TABLE_NAME t
)
WHERE names IS NOT EMPTY
ORDER BY CARDINALITY( names ) DESC, ID;
Output:
ID COL1 COL2 COL3 COL4
---------- ------ ------ ------ ----
1 Andrew Joan Bach Mike
3 Joan Arch Donnie
4 Joan Andrew Lyx
This is untested, but I think it'll work. First, you need a function that will split a search string by spaces into a table:
CREATE function [dbo].[SplitSpace] (#StringList varchar(4000))
RETURNS #Result Table(Value varchar(50))
AS
BEGIN
DECLARE #x XML
SELECT #X = CAST('<A>' + REPLACE(#StringList, ' ', '</A><A>') + '</A>' AS XML)
INSERT INTO #Result
SELECT t.value('.', 'varchar(50)') as inVal
FROM #X.nodes('/A') AS x(t)
RETURN
END
This function will come in handy elsewhere, too, and you can easily modify it to split by commas or any other value, if needed.
Next, you need to create a query that returns the desired results (when you're done testing, you can convert this to a stored procedure and accept the search string as a parameter):
DECLARE #SearchString varchar(255) = 'Bach Joan Mike'
DECLARE #SearchTable TABLE(Value varchar(50))
INSERT INTO #SearchTable
SELECT DISTINCT Value
FROM SplitSpace(#SearchString)
SELECT DISTINCT Col1, Col2, Col3, Col4
FROM MyTable M
JOIN #SearchTable S
ON S.Value = Col1
OR S.Value = Col2
OR S.Value = Col3
OR S.Value = Col4
CROSS APPLY
(
SELECT COUNT(*) AS [Number of Hits]
FROM #SearchTable
WHERE Value = M.Col1
OR Value = M.Col2
OR Value = M.Col3
OR Value = M.Col4
) t
ORDER BY t.[Number of Hits] DESC
Basically, you're saying "give me all the records where one or more of the "name" columns exists in the search string.
Then, you're saying, with the Cross Apply, "for each row, tell me how many hits I have". Then, all you have to do is order by the number of hits and you're all set.
Note: it's possible that someone could enter "Andrew Andrew Bach" as a search string. If they did so, you would get 2 hits for each column that has Andrew in it and only one for each one that had Bach. That is why you select distinct value from the function-returned table; it eliminates those duplicates. Also, if you have multiple matches, you would get that record returned twice, since it's an inner join, so you select distinct col1, col2, col3, col4 from those results, to eliminate those duplicates, as well.
Please let me know if you have any questions.

SQL Query to get name that appear twice

I have the below Table
Table1
Emp ID | Emp Name
001 | ABC
002 | DEF
003 | GHI
004 | ABC
005 | XYZ
I am trying to get EMP ID and Emp Name where Emp Name is same but Emp ID is different. There is primary key in the table
Here the output will be
Emp ID | Emp Name
001 | ABC
004 | ABC
You didn't specify your DBMS, so this is ANSI SQL:
select emp_id, emp_name
from (
select emp_id, emp_name,
count(*) over (partition by emp_name) as name_count
from employee
) t
where name_count > 1^;
Group by Emp_Name, select records having count Emp_ID more than one and then select all records from table having such Emp_Name:
select Emp_ID, Emp_Name
from Table1
where Emp_Name in
(
select Emp_Name
from Table1
group by Emp_Name
having count(Emp_ID) > 1
)
Please try with below.
SELECT T1.* FROM
TABLE1 T1
JOIN
(
SELECT EMP_NAME, COUNT(EMP_ID) FROM TABLE1 GROUP BY EMP_NAME HAVING COUNT(EMP_ID) > 1
) T2 ON T1.EMP_NAME = T2.EMP_NAME
I have the same problem yesterday :) and i believe below code will give you what you want.
Use INNER JOIN to make a self join query, then use HAVING clause.
CREATE TABLE #Table1 (Emp_ID int, Emp_Name varchar(50))
INSERT INTO [#Table1]
(
[Emp_ID],
[Emp_Name]
)
SELECT '001','ABC'
UNION ALL SELECT '002','DEF'
UNION ALL SELECT '003','GHI'
UNION ALL SELECT '004','ABC'
UNION ALL SELECT '005','XYZ'
SELECT [t1].[Emp_ID], [t1].[Emp_Name] FROM [#Table1] t1
INNER JOIN
(
SELECT [#Table1].[Emp_Name] FROM [#Table1]
GROUP BY [#Table1].[Emp_Name]
HAVING COUNT([#Table1].[Emp_ID]) > 1
) t2
ON [t1].[Emp_Name] = [t2].[Emp_Name]
DROP TABLE [#Table1]
Below is the result:
Emp_ID Emp_Name
1 ABC
4 ABC
SQL Fiddle Demo - Click here
Another EXISTS version, but without any aggregating:
select t1.*
from tablename t1
where exists (select 1 from tablename t2
where t2.Emp_Name = t1.Emp_Name
and t2.Emp_ID <> t1.Emp_ID)
May speed up things a bit. Otherwise I'd try self join version:
select t1.*
from tablename t1
join tablename t2 on t2.Emp_Name = t1.Emp_Name
and t2.Emp_ID <> t1.Emp_ID
Perhaps, depending on data, you need to do SELECT DISTINCT.

Need Oracle Query Tune for order by

I have two tables:
METHOD_TYPES
---- ----------------
ID Methods_Type
---- ----------------
1 public
2 ALL_Methods
3 private1235678
4 social
METHOD_TABLE
-------- ----------------- ----------
Ser_ID Ser_Method_Type Emp_Name
-------- ----------------- ----------
1 (null) AAAA
2 (null) BBBB
3 All_Methods Rama
4 social Raja
5 private12345678 Rakesh
I used the below query for the ORDER BY:
SELECT SUBSTR(Methods_Type, 1, 10) AS disMisType
FROM METHOD_TABLE MET
LEFT JOIN METHOD_TYPES TRMT
ON MET.Ser_Method_Type = TRMT.Methods_Type
ORDER BY (NLSSORT(MET.Ser_Method_Type, 'NLS_SORT=binary_ai')) DESC NULLS FIRST;
OUTPUT:
(null)
All_Methods
(null)
social
private12345678
But I need to order all the nulls first.
Kindly provide the exact query.
Using the data you provided - and adding in the extra columns, I get:
with method_types as (select 1 id, 'public' methods_type from dual union all
select 2 id, 'ALL_Methods' methods_type from dual union all
select 3 id, 'private1235678' methods_type from dual union all
select 4 id, 'social' methods_type from dual),
method_table as (select 1 ser_id, null ser_method_type, 'AAAA' emp_name from dual union all
select 2 ser_id, null ser_method_type, 'BBBB' emp_name from dual union all
select 3 ser_id, 'All_Methods' ser_method_type, 'Rama' emp_name from dual union all
select 4 ser_id, 'social' ser_method_type, 'Raja' emp_name from dual union all
select 5 ser_id, 'private12345678' ser_method_type, 'Rakesh' emp_name from dual)
select substr(trmt.methods_type,1,10) as dismistype,
met.*,
trmt.*
from method_table met
left join method_types trmt on (met.ser_method_type = trmt.methods_type)
order by (nlssort(met.ser_method_type, 'NLS_SORT=binary_ai')) desc nulls first;
DISMISTYPE SER_ID SER_METHOD_TYPE EMP_NAME ID METHODS_TYPE
------------------------------ ---------- --------------- -------- ---------- --------------
1 AAAA
2 BBBB
social 4 social Raja 4 social
5 private12345678 Rakesh
3 All_Methods Rama
which is not what your expected output shows, but it does maybe explain why you see nulls apparently out of order in your results - you're selecting the trmt.methods_type column, but ordering by the met.ser_method_type column. If there aren't any rows in the method_types table matching those in the method_table, then of course you will see nulls, but because there IS a value in the method_table, they may well be displayed after rows that do have a value.
Perhaps all you need to do is to change the column being selected
from substr(trmt.methods_type,1,10)
to substr(met.ser_method_type,1,10)
or change the order clause
from nlssort(met.ser_method_type, 'NLS_SORT=binary_ai')
to nlssort(trmt.methods_type, 'NLS_SORT=binary_ai')
I'm not sure why your query is not working, but you can have a more explicit order by:
ORDER BY (CASE WHEN MET.Ser_Method_Type IS NULL THEN 1 ELSE 2 END),
NLSSORT(MET.Ser_Method_Type, 'NLS_SORT=binary_ai') DESC
You can create a CASE Column only for order:
select SUBSTR(Methods_Type,1,10)AS disMisType,
SUBSTR(CASE WHEN Methods_Type IS NULL THEN '0' ELSE Methods_Type END ,1,10) AS disMisTypeORDER
FROM METHOD_TABLE MET
LEFT JOIN METHOD_TYPES TRMT
ON MET.Ser_Method_Type = TRMT.Methods_Type
ORDER BY disMisTypeORDER