Oracle unexpected sorting behaviour - sql

Consider the Following:
CREATE TABLE TestPersons
(
PersonID int,
Name varchar(255),
FirstName varchar(255),
Address varchar(255)
)
insert into TestPersons(PersonID, Name, FirstName , Address) Values (1, 'name1', 'firstname1', 'adress1');
insert into TestPersons(PersonID, Name, FirstName , Address) Values (2, 'name2', 'firstname2', 'adress2');
insert into TestPersons(PersonID, Name, FirstName , Address) Values (3, 'name3', 'firstname3', 'adress3');
insert into TestPersons(PersonID, Name, FirstName , Address) Values (4, 'name4', 'firstname4', 'adress4');
First Query (works fine):
SELECT name, firstname, personid, count(name), row_number() over (ORDER BY name, firstname) as rn
FROM testpersons
GROUP BY name, firstname, personid
returns:
name1 firstname1 1 1 1
name2 firstname2 2 1 2
name3 firstname3 3 1 3
name4 firstname4 4 1 4
Second Query (unexpected result):
SELECT name, firstname, personid, count(name), row_number() over (ORDER BY name desc, firstname desc) as rn
FROM testpersons
GROUP BY name, firstname, personid
returns for all rows name4 and firstname4:
name4 firstname4 4 1 1
name4 firstname4 3 1 2
name4 firstname4 2 1 3
name4 firstname4 1 1 4
I would expect a changed order of the result from the first query.
Any Idea why is that?
Output of select * from v$version is as follows:
Oracle Database 12c Release 12.1.0.1.0 - 64bit Production 0
PL/SQL Release 12.1.0.1.0 - Production 0
"CORE 12.1.0.1.0 Production" 0
TNS for 64-bit Windows: Version 12.1.0.1.0 - Production 0
NLSRTL Version 12.1.0.1.0 - Production 0

I don't know what you are talking about. Below is a direct copy and paste from my session where I entered the commands as you posted them. I get the correct result from the second query.
Is there something you didn't tell us? What version of Oracle are you using? How are you interacting with it? What is your front-end? Do you send your queries directly or through some app?
What you see below is Oracle 11.2 XE, SQL*Plus front-end.
SQL> CREATE TABLE TestPersons
2 (
3 PersonID int,
4 Name varchar(255),
5 FirstName varchar(255),
6 Address varchar(255)
7 );
Table created.
SQL> insert into TestPersons(PersonID, Name, FirstName , Address) Values (1, 'name1', 'firstname1', 'adress1');
1 row created.
SQL> insert into TestPersons(PersonID, Name, FirstName , Address) Values (2, 'name2', 'firstname2', 'adress2');
1 row created.
SQL> insert into TestPersons(PersonID, Name, FirstName , Address) Values (3, 'name3', 'firstname3', 'adress3');
1 row created.
SQL> insert into TestPersons(PersonID, Name, FirstName , Address) Values (4, 'name4', 'firstname4', 'adress4');
1 row created.
SQL> column name format a10
SQL> column firstname format a18
SQL> SELECT name, firstname, personid, count(name), row_number() over (ORDER BY name, firstname) as rn
2 FROM testpersons
3 GROUP BY name, firstname, personid;
NAME FIRSTNAME PERSONID COUNT(NAME) RN
---------- ------------------ ---------- ----------- ----------
name1 firstname1 1 1 1
name2 firstname2 2 1 2
name3 firstname3 3 1 3
name4 firstname4 4 1 4
4 rows selected.
SQL> SELECT name, firstname, personid, count(name), row_number() over (ORDER BY name desc, firstname desc) as rn
2 FROM testpersons
3 GROUP BY name, firstname, personid;
NAME FIRSTNAME PERSONID COUNT(NAME) RN
---------- ------------------ ---------- ----------- ----------
name4 firstname4 4 1 1
name3 firstname3 3 1 2
name2 firstname2 2 1 3
name1 firstname1 1 1 4
4 rows selected.

Related

Multiple rows to multiple columns for co-applicant name and address for a unique ID

This is the query
SELECT b.ID, e.customername AS "Applicant name",
f.address AS "Applicant address",
x.customername AS "Co-Applicant name",
x.address AS "Co-Applicant address"
FROM table_1 b,
table_2 e,
table_3 f,
(SELECT b.customername, g.agreementid, a.address
FROM table_2 g, table_4 x, table_2 b, table_3 a
WHERE g.ID = x.ID
AND b.customerid = x.custid
AND b.customerid = a.custid
AND x.flag <> 'G') x
WHERE b.custid = e.customerid
AND f.custid = b.lesseeid
AND f.bptype = 'LS'
AND f.mailingaddress = 'Y'
AND b.ID = x.ID
AND b.ID='101'
The data is coming in below format.
+-----+-------+----------+--------------+----------+
| ID | name | address | co-applicant | address |
+-----+-------+----------+--------------+----------+
| 101 | aamir | address1 | rahul | London |
| 101 | aamir | address1 | vijay | Paris |
| 101 | aamir | address1 | sanjay | New York |
+-----+-------+----------+--------------+----------+
I need the data in below format
![ID name address name_1 address name_2 address
101 aamir address1 rahul London vijay Paris
102 Anil address2 Suyash Mumbai Rajesh Delhi Prakash Kolkata]1
You can use PIVOT as following:
SQL> WITH DATAA AS
2 (
3 SELECT 101 ID, 'aamir' name, 'address1' address, 'rahul' co_applicant, 'london' co_address FROM DUAL UNION ALL
4 SELECT 101, 'aamir', 'address1', 'vijay', 'Paris' FROM DUAL UNION ALL
5 SELECT 101, 'aamir', 'address1', 'sanjay', 'New York' FROM DUAL
6 )
7 -- YOUR QUERY STARTS FROM HERE
8 SELECT * FROM
9 (
10 SELECT
11 T.*,
12 ROW_NUMBER() OVER(ORDER BY NULL) AS RN
13 FROM DATAA T
14 ) PIVOT (
15 MAX ( CO_APPLICANT ) AS NAME, MAX ( CO_ADDRESS ) AS ADDRESS
16 FOR RN IN ( 1, 2,3 )
17 );
ID NAME ADDRESS 1_NAME 1_ADDRES 2_NAME 2_ADDRES 3_NAME 3_ADDRES
---------- ----- -------- ------ -------- ------ -------- ------ --------
101 aamir address1 rahul london vijay Paris sanjay New York
SQL>
Note: It will generate only 3 combinations of name and address as oracle do not allow dynamic columns in the query. If there is more than 3 co-applicant exist then it will take only data of 3 co-applicants.
-- UPDATE --
Use PARTITION BY clause in ROW_NUMBER if you multiple IDs as following:
SQL> WITH DATAA AS
2 (
3 SELECT 101 ID, 'aamir' name, 'address1' address, 'rahul' co_applicant, 'london' co_address FROM DUAL UNION ALL
4 SELECT 101, 'aamir', 'address1', 'vijay', 'Paris' FROM DUAL UNION ALL
5 SELECT 101, 'aamir', 'address1', 'sanjay', 'New York' FROM DUAL UNION ALL
6 SELECT 102 ID, 'Tejash' name, 'address2' address, 'chetan' co_applicant, 'london' co_address FROM DUAL UNION ALL
7 SELECT 102, 'Tejash', 'address2', 'nirav', 'Paris' FROM DUAL UNION ALL
8 SELECT 102, 'Tejash', 'address2', 'pulkit', 'New York' FROM DUAL
9 )
10 -- YOUR QUERY STARTS FROM HERE
11 SELECT * FROM
12 (
13 SELECT
14 T.*,
15 ROW_NUMBER() OVER(PARTITION BY ID ORDER BY NULL) AS RN
16 FROM DATAA T
17 ) PIVOT (
18 MAX ( CO_APPLICANT ) AS NAME, MAX ( CO_ADDRESS ) AS ADDRESS
19 FOR RN IN ( 1, 2,3 )
20 );
ID NAME ADDRESS 1_NAME 1_ADDRES 2_NAME 2_ADDRES 3_NAME 3_ADDRES
---------- ------ -------- ------ -------- ------ -------- ------ --------
101 aamir address1 rahul london vijay Paris sanjay New York
102 Tejash address2 chetan london nirav Paris pulkit New York
SQL>
Cheers!!
I guess you can first do the conditional aggregation and then can join back with table -
WITH DATAA AS (SELECT 101 ID, 'aamir' name, 'address1' address, 'rahul' co_applicant, 'london' co_address FROM DUAL
UNION ALL
SELECT 101, 'aamir', 'address1', 'vijay', 'Paris' FROM DUAL
UNION ALL
SELECT 101, 'aamir', 'address1', 'sanjay', 'New York' FROM DUAL
),
TEMP AS (select D.*, ROW_NUMBER() OVER(PARTITION BY name ORDER BY co_applicant) RN from DATAA D)
SELECT ID, NAME, ADDRESS
,MAX(CASE WHEN RN = 1 THEN CO_APPLICANT ELSE NULL END) AS name_1
,MAX(CASE WHEN RN = 1 THEN CO_ADDRESS ELSE NULL END) AS ADDRESS_1
,MAX(CASE WHEN RN = 2 THEN CO_APPLICANT ELSE NULL END) AS name_2
,MAX(CASE WHEN RN = 2 THEN CO_ADDRESS ELSE NULL END) AS ADDRESS_2
,MAX(CASE WHEN RN = 3 THEN CO_APPLICANT ELSE NULL END) AS name_3
,MAX(CASE WHEN RN = 3 THEN CO_ADDRESS ELSE NULL END) AS ADDRESS_3
FROM TEMP
GROUP BY ID, NAME, ADDRESS;
With the clarification as SQL tables represent unordered sets until you specify a deterministic order clause. So I use an order by clause as co_applicant name. Thus from your sample data Sanjay is coming on 2nd col and Vihjay is coming in 3rd col.
Here is the fiddle.

Counting how many fields are the same in a row

I have data in a table organised as follows:
ID, name1, name2, name3, name4, name5, name6
Sample data
select ID, name1, name2, name3, name4, name5, name6
from datatable
123, bob, mark, jane, bob, jane, fred
124, mark, mark, mark, bob, bob, bob
and I need to end up with something like
123, bob, 2
123, mark, 1
123, jane, 2
123, fred, 1
124, mark, 3
124, bob, 3
Where the count is the number of times a name appears in the record. Is this possible?
The real reason you're having a problem here is because you have de-normalised data. Instead of 6 Name columns, you should have 1 column (called [Name]) and then another column to denote the "number" (ID?).
You can do this on the fly, however, using VALUES to Unpivot your data, and then perform a COUNT, but I strongly recommend you fix your table design in the long run:
SELECT DT.ID,
V.[Name],
COUNT(V.[Name]) AS Names
FROM dbo.DataTable DT
CROSS APPLY (VALUES(DT.name1),DT.(name2),(DT.name3),(DT.name4),(DT.name5),(DT.name6))V([Name])
GROUP BY DT.ID,
V.[Name];
Try this:
declare #tbl table (ID int, name1 varchar(10), name2 varchar(10), name3 varchar(10), name4 varchar(10), name5 varchar(10), name6 varchar(10))
insert into #tbl values
(123, 'bob', 'mark', 'jane', 'bob', 'jane', 'fred'),
(124, 'mark', 'mark', 'mark', 'bob', 'bob', 'bob')
select id, [name], count(*) cnt from (
select ID, name1 [name] from #tbl
union all
select ID, name2 from #tbl
union all
select ID, name3 from #tbl
union all
select ID, name4 from #tbl
union all
select ID, name5 from #tbl
union all
select ID, name6 from #tbl
) a group by id, [name]
Using a union approach:
WITH cte AS (
SELECT ID, name1 AS name FROM yourTable UNION ALL
SELECT ID, name2 FROM yourTable UNION ALL
SELECT ID, name3 FROM yourTable UNION ALL
SELECT ID, name4 FROM yourTable UNION ALL
SELECT ID, name5 FROM yourTable UNION ALL
SELECT ID, name6 FROM yourTable
)
SELECT
ID,
name,
COUNT(*) AS Count
FROM cte
GROUP BY
ID,
name
ORDER BY
ID,
name;
Demo

How to force oracle to execute subquery for every row?

I have two tables and I want to select random value from the second table for each record in the first one. The problem is that it seems like this value is always the same for every row meaning that it is executed only once for all rows. How could I achieve that?
create table first_table(name varchar2(100));
insert into first_tablevalues('John');
insert into first_tablevalues('Jessie');
insert into first_tablevalues('Jack');
select * from second_table;
create table second_table(id number);
insert into second_table(id) values(1);
insert into second_table(id) values(2);
insert into second_table(id) values(3);
insert into second_table(id) values(4);
insert into second_table(id) values(5);
insert into second_table(id) values(6);
Then I executed this query, but for every name in the first table I get the same number.
SELECT NAME,
(SELECT id
FROM (SELECT id
FROM second_table
ORDER BY dbms_random.value)
WHERE rownum = 1)
FROM first_table;
A correlation clause fixes the problem:
SELECT NAME,
(SELECT id
FROM (SELECT id
FROM second_table st
ORDER BY dbms_random.value
)
WHERE rownum = 1 AND ft.name IS NOT NULL
)
FROM first_table ft;
Here is a rextester.
This is ugly but - kind of works.
SQL> WITH ri
2 AS (SELECT s.id,
3 ROW_NUMBER () OVER (ORDER BY DBMS_RANDOM.VALUE) rn,
4 COUNT (*) OVER (ORDER BY NULL) cnt
5 FROM second_table s),
6 rn
7 AS (SELECT f.name,
8 ROW_NUMBER () OVER (ORDER BY DBMS_RANDOM.VALUE (1, ri.cnt))
9 rn
10 FROM first_table f JOIN ri ON 1 = 1)
11 SELECT rn.name, MAX (ri.id) id
12 FROM rn JOIN ri ON rn.rn = ri.rn
13 GROUP BY rn.name;
NAME ID
---------- ----------
Jack 3
John 2
Jessie 6
SQL> /
NAME ID
---------- ----------
John 5
Jack 6
Jessie 4
SQL> /
NAME ID
---------- ----------
Jack 6
John 2
Jessie 4
SQL> /
NAME ID
---------- ----------
Jack 5
John 6
Jessie 3
SQL> /
NAME ID
---------- ----------
John 6
Jack 3
Jessie 1
SQL>

Find duplicates out of multiple columns

I have a tricky sql problem. Let me qive you an example
ID1 Name Name2 Name3 Name4
100 Albert Kevin Jon Alex
101 Albert Jon Kevin Alex
102 Albert Georg Alex Babera
103 Albert Stefany
Lets say ID1 gives me a project ID and Name is the main person (Albert). Name2-4 are subgroups of people who worked with Albert. Now I want to count matches between this subgroups. First I want to know exact matches. For example between 100 and 101.
Second is it possible to count how many names matches? Like one match between 101 and 100.
Thanks in advance
I know it is long and not bulletproof but it kind of does the job.
WITH source_t AS
(
SELECT 100 id, 'Albert' name, 'Kevin' name2, 'Jon' name3, 'Alex' name4 FROM DUAL UNION ALL
SELECT 101, 'Albert', 'Jon', 'Kevin', 'Alex' FROM DUAL UNION ALL
SELECT 102, 'Albert', 'Georg', 'Alex', 'Babera' FROM DUAL UNION ALL
SELECT 103, 'Albert', 'Stefany', NULL, NULL FROM DUAL
)
, tab_1 AS
(
SELECT id, name, name2 FROM source_t UNION ALL
SELECT id, name, name3 FROM source_t UNION ALL
SELECT id, name, name4 FROM source_t
)
, tab_2 AS
(
SELECT id
, name
, name2
, ROW_NUMBER() OVER (PARTITION BY id, name ORDER BY name2) AS r_number
FROM tab_1
)
, tab_3 AS
(
SELECT id
, name
, MAX(CASE WHEN r_number = 1 THEN name2 END) AS name2
, MAX(CASE WHEN r_number = 2 THEN name2 END) AS name3
, MAX(CASE WHEN r_number = 3 THEN name2 END) AS name4
FROM tab_2
GROUP BY
id
, name
)
SELECT tab_3.id
, tab_3.name
, tab_3.name2
, tab_3.name3
, tab_3.name4
, tab_4.n_count
FROM tab_3
LEFT JOIN
(
SELECT name
, name2
, name3
, name4
, COUNT(1) AS n_count
FROM tab_3
GROUP BY
name
, name2
, name3
, name4
) tab_4
ON tab_3.name = tab_4.name
and NVL(tab_3.name2, 'NULL') = NVL(tab_4.name2, 'NULL')
and NVL(tab_3.name3, 'NULL') = NVL(tab_4.name3, 'NULL')
and NVL(tab_3.name4, 'NULL') = NVL(tab_4.name4, 'NULL')
;
/*
102 Albert Alex Babera Georg 1
103 Albert Stefany NULL NULL 1
101 Albert Alex Jon Kevin 2
100 Albert Alex Jon Kevin 2
*/

Convert columns to rows in SQL [duplicate]

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