My table where I am storing my information is organized as follows:
ID JobID Project FirstName LastName
One job may have multiple projects and within the projects there may be up to 5 contacts. I would like to display the information in the following way:
ID JobID Project FirstName1 LastName1 FirstName2 LastName2.........FirstName5 LastName 5
Can a transform query be used for this or do you need to create another table and insert the information?
Thanks
Yes, what you describe is feasible. For test data in a table named [contactInfo]
ID JobID Project FirstName LastName
-- ----- ------- --------- --------
1 1 1 Homer Simpson
2 1 1 Marge Simpson
3 1 1 Lisa Simpson
4 1 2 Ned Flanders
start by creating a saved query in Access named [contactInfoRanked]
SELECT contactInfo.*, ranks.rowRank
FROM
contactInfo
INNER JOIN
(
SELECT t1.ID, Count(*) AS rowRank
FROM
contactInfo AS t1
INNER JOIN
contactInfo AS t2
ON t1.JobID = t2.JobID
AND t1.Project = t2.Project
AND t1.ID >= t2.ID
GROUP BY t1.ID
) AS ranks
ON contactInfo.ID = ranks.ID
which returns
ID JobID Project FirstName LastName rowRank
-- ----- ------- --------- -------- -------
1 1 1 Homer Simpson 1
2 1 1 Marge Simpson 2
3 1 1 Lisa Simpson 3
4 1 2 Ned Flanders 1
So now the query
SELECT
JobID,
Project,
"FirstName1" AS columnName,
FirstName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 1
UNION ALL
SELECT
JobID,
Project,
"LastName1" AS columnName,
LastName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 1
UNION ALL
SELECT
JobID,
Project,
"FirstName2" AS columnName,
FirstName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 2
UNION ALL
SELECT
JobID,
Project,
"LastName2" AS columnName,
LastName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 2
UNION ALL
SELECT
JobID,
Project,
"FirstName3" AS columnName,
FirstName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 3
UNION ALL
SELECT
JobID,
Project,
"LastName3" AS columnName,
LastName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 3
UNION ALL
SELECT
JobID,
Project,
"FirstName4" AS columnName,
FirstName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 4
UNION ALL
SELECT
JobID,
Project,
"LastName4" AS columnName,
LastName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 4
UNION ALL
SELECT
JobID,
Project,
"FirstName5" AS columnName,
FirstName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 5
UNION ALL
SELECT
JobID,
Project,
"LastName5" AS columnName,
LastName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 5
returns
JobID Project columnName columnValue
----- ------- ---------- -----------
1 1 FirstName1 Homer
1 2 FirstName1 Ned
1 1 LastName1 Simpson
1 2 LastName1 Flanders
1 1 FirstName2 Marge
1 1 LastName2 Simpson
1 1 FirstName3 Lisa
1 1 LastName3 Simpson
and we can just use that as the source for our crosstab query
TRANSFORM First(columnValue) AS colVal
SELECT JobID, Project
FROM
(
SELECT
JobID,
Project,
"FirstName1" AS columnName,
FirstName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 1
UNION ALL
SELECT
JobID,
Project,
"LastName1" AS columnName,
LastName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 1
UNION ALL
SELECT
JobID,
Project,
"FirstName2" AS columnName,
FirstName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 2
UNION ALL
SELECT
JobID,
Project,
"LastName2" AS columnName,
LastName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 2
UNION ALL
SELECT
JobID,
Project,
"FirstName3" AS columnName,
FirstName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 3
UNION ALL
SELECT
JobID,
Project,
"LastName3" AS columnName,
LastName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 3
UNION ALL
SELECT
JobID,
Project,
"FirstName4" AS columnName,
FirstName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 4
UNION ALL
SELECT
JobID,
Project,
"LastName4" AS columnName,
LastName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 4
UNION ALL
SELECT
JobID,
Project,
"FirstName5" AS columnName,
FirstName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 5
UNION ALL
SELECT
JobID,
Project,
"LastName5" AS columnName,
LastName AS columnValue
FROM contactInfoRanked
WHERE rowRank = 5
)
GROUP BY JobID, Project
PIVOT columnName
IN
(
"FirstName1","LastName1",
"FirstName2","LastName2",
"FirstName3","LastName3",
"FirstName4","LastName4",
"FirstName5","LastName5"
)
which returns
JobID Project FirstName1 LastName1 FirstName2 LastName2 FirstName3 LastName3 FirstName4 LastName4 FirstName5 LastName5
----- ------- ---------- --------- ---------- --------- ---------- --------- ---------- --------- ---------- ---------
1 1 Homer Simpson Marge Simpson Lisa Simpson
1 2 Ned Flanders
Related
I have a table named Employee_audit with following schema,
emp_audit_id
eid
name
salary
...
1
1
Daniel
1000
...
2
1
Dani
1000
...
3
1
Danny
3000
...
My goal is to write a SQL query which will return in following format, considering the first row also as changed value from null.
columnName
oldValue
newValue
name
null
Daniel
salary
null
1000
name
Daniel
Dani
name
Dani
Danny
salary
1000
3000
...
...
...
Finally reached to below solution
CREATE TABLE Employee_audit (
emp_audit_id int,
eid int,
name varchar(50),
salary int,
department varchar(50)
)
insert into Employee_audit (emp_audit_id, eid, name, salary,department)
values
(1, 1, 'Daniel', 1000,'ROP'),
(2, 1, 'Dani', 1000,'ROP'),
(3, 1, 'Danny', 3000,'ROP');
with diffs as (
select 'name' colName, emp_audit_id, eid, lag(name, 1, null) over (partition by eid order by emp_audit_id) oldValue, name newValue
from Employee_audit
union all
select 'salary', emp_audit_id, eid, cast(lag(salary, 1, null) over (partition by eid order by emp_audit_id) as varchar), cast(salary as varchar) newValue
from Employee_audit
union all
...
)
select *
from diffs
where oldValue <> newValue or oldValue is null
order by emp_audit_id, eid
Returns:
emp_audit_id
columnName
oldValue
newValue
1
name
NULL
Daniel
1
salary
NULL
1000.00
2
name
Daniel
Dani
3
name
Dani
Danny
3
salary
1000.00
3000.00
...
...
...
...
But the problem is, the query is very slow because to track 10 fields we have to write 10 union all.
How can I more optimize the query like in a single scan how can I do it?
Here is an option that will dynamically unpivot your data without actually using Dynamic SQL.
Example
;with cte as (
Select emp_audit_id
,eid
,[key]
,newValue=[value]
,oldvalue = lag(value) over (partition by eid,[key] order by emp_audit_id)
From Employee_audit A
Cross Apply ( Select [key],[value] From OpenJson( (Select A.* For JSON Path,Without_Array_Wrapper ) ) ) B
Where [key] not in ('emp_audit_id','eid')
)
Select emp_audit_id
,columName = [key]
,oldvalue
,newvalue
From cte
Where newvalue<>oldvalue or oldvalue is null
Returns
emp_audit_id columName oldvalue newvalue
1 department NULL ROP
1 name NULL Daniel
2 name Daniel Dani
3 name Dani Danny
1 salary NULL 1000
3 salary 1000 3000
I would use apply:
select t.emp_audit_id, v.columnName, v.newValue,
lag(v.newValue) over (partition by eid, columnName order by emp_audit_id) as oldValue
from some_table t cross apply
(values ('name', t.name),
('salary', t.salary),
. . .
) v (columnName, newValue);
If you need to cast the values so they are strings, that goes in the values clause:
select t.emp_audit_id, v.columnName, v.newValue,
lag(v.newValue) over (partition by eid, ColumnName order by emp_audit_id) as oldValue
from some_table t cross apply
(values ('name', t.name),
('salary', cast(t.salary as varchar(255))),
. . .
) v (columnName, newValue);
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
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.
I have 4 tables (say emp1, emp2, emp3, emp4) with nearly identical columns. I want to fetch details
(select empid, empname from emp1 where empid = '1' )
UNION
(select empid, empname from emp2 where empid = '1')
UNION
(select empid, empname from emp3 where empid = '1')
UNION
(select empid, empname from emp4 where empid = '1')
The thing is if I got result from first query (emp1) it should ignore queries below (emp2, emp3, emp4). If I get a result from emp2, it should ignore (emp3, emp4) and so on.
Remember in emp1, emp2, emp3, emp4 there an be different empname associated with same empid. That's why union giving all the results. In that case I have to prefer result from uppermost table i.e emp1 > emp2 > emp3. I also tried using 'case' but things are not working for me.
Sample data
emp1
1 deepak
emp2
1 nitin
emp3
1 sateesh
emp4
1 chandra
and expected result is
1 deepak
I hope I am clear to you. please help me thank you
You can add an arbitrary column to specify the priority.
SQL Fiddle
;WITH Cte AS(
SELECT *, N = 1 FROM emp1 WHERE empId = 1 UNION ALL
SELECT *, N = 2 FROM emp2 WHERE empId = 1 UNION ALL
SELECT *, N = 3 FROM emp3 WHERE empId = 1 UNION ALL
SELECT *, N = 4 FROM emp4 WHERE empId = 1
)
,CteRN AS(
SELECT *, RN = ROW_NUMBER() OVER(ORDER BY N) FROM Cte
)
SELECT
empId, empName
FROM CteRN
WHERE RN = 1
Basically, you want to prioritize results from emp1, then emp2 and so on. This is where the arbitrary column N comes in. You want to rank them in order of priority. The result of the first CTE is:
empId empName N
----------- ---------- -----------
1 deepak 1
1 nitin 2
1 sateesh 3
1 chandra 4
Then you use ROW_NUMBER to add a sequential number to each rows. The second CTE, CteRN will give you:
empId empName N RN
----------- ---------- ----------- --------
1 deepak 1 1
1 nitin 2 2
1 sateesh 3 3
1 chandra 4 4
Laslt, you only want the row with the least RN, so you add a WHERE RN = 1 clause. The final result would be:
empId empName
----------- ----------
1 deepak
Additionally, you can add a PARTITION BY empId on RN = ROW_NUMBER() OVER(ORDER BY N)
Based on your updated question, since empid should not be repeated from other tables, you can try something like this.
select empid,empname
from emp1
UNION ALL
select empid,empname
from emp2 e
WHERE NOT EXISTS( SELECT empid FROM emp1 ee WHERE ee.empid = e.empid)
UNION ALL
select empid,empname
from emp3 e
WHERE NOT EXISTS( SELECT empid FROM emp1 ee WHERE ee.empid = e.empid)
AND NOT EXISTS( SELECT empid FROM emp2 ee WHERE ee.empid = e.empid)
UNION ALL
select empid,empname from emp4 e
WHERE NOT EXISTS( SELECT empid FROM emp1 ee WHERE ee.empid = e.empid)
AND NOT EXISTS( SELECT empid FROM emp2 ee WHERE ee.empid = e.empid)
AND NOT EXISTS( SELECT empid FROM emp3 ee WHERE ee.empid = e.empid)
This isn't pretty, but it works.
INSERT INTO emp3
SELECT 1, 'pwalton'
INSERT INTO emp4
SELECT 2, 'jimmy'
DECLARE #myTable TABLE (empid INT, empname VARCHAR(255))
INSERT INTO #myTable
SELECT empid, empname FROM emp1
IF NOT EXISTS (SELECT 1 FROM #myTable)
BEGIN
INSERT INTO #myTable
SELECT empid, empname FROM emp2
END
IF NOT EXISTS (SELECT 1 FROM #myTable)
BEGIN
INSERT INTO #myTable
SELECT empid, empname FROM emp3
END
IF NOT EXISTS (SELECT 1 FROM #myTable)
BEGIN
INSERT INTO #myTable
SELECT empid, empname FROM emp4
END
SELECT * FROM #myTable
--results: 1 pwalton
I have a table customer in the database as below.
ID UID Address1 Name code
10 5 A Jac 683501
11 5 B Joe 727272
13 6 C mat 373737
first two records (10,11) have a common uID -5 . These two records can be considered as a single unit.
fourth record is having a separate UID , so it is a separate unit
I need to produce an output in a csv file such a way that
ID UID Name code Address1 Name2 Code2
10 5 jac 683501 A Joe 727272
13 6 mat 373737 C
Name2 and code2 values are from the second row, since UID is same for first two records, we can considered it as a single unit.
Can anyone give hints to query for generating these records.
This process to transform data from rows into columns is known as a PIVOT. There are several ways that this can be done.
You can use a row_number() along with an aggregate function with a CASE expression:
select min(id),
uid,
max(case when seq = 1 then name end) Name,
max(case when seq = 1 then code end) Code,
max(case when seq = 1 then Address1 end) Address1,
max(case when seq = 2 then name end) Name2,
max(case when seq = 2 then code end) code2,
max(case when seq = 2 then Address1 end) Address1_2
from
(
select id, uid, address1, name, code,
row_number() over(partition by uid order by id) seq
from yourtable
) d
group by uid;
See SQL Fiddle with Demo.
You could use both the UNPIVOT and the PIVOT function:
select id, uid,
name1, code1, address1, name2, code2, address2
from
(
select id, uid, col+cast(seq as varchar(10)) col, value
from
(
select
(select min(id)
from yourtable t2
where t.uid = t2.uid) id,
uid,
cast(address1 as varchar(20)) address,
cast(name as varchar(20)) name,
cast(code as varchar(20)) code,
row_number() over(partition by uid order by id) seq
from yourtable t
) d
unpivot
(
value
for col in (address, name, code)
) unpiv
) src
pivot
(
max(value)
for col in (name1, code1, address1, name2, code2, address2)
) piv;
See SQL Fiddle with Demo.
Finally if you have an unknown number of values for each uid, then you can use dynamic SQL to get the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(col+(cast(seq as varchar(10))))
from
(
select row_number() over(partition by uid order by id) seq
from yourtable
) d
cross apply
(
select 'name', 1 union all
select 'code', 2 union all
select 'address', 3
) c (col, so)
group by seq, col, so
order by seq, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT id, uid,' + #cols + '
from
(
select id, uid, col+cast(seq as varchar(10)) col, value
from
(
select
(select min(id)
from yourtable t2
where t.uid = t2.uid) id,
uid,
cast(address1 as varchar(20)) address,
cast(name as varchar(20)) name,
cast(code as varchar(20)) code,
row_number() over(partition by uid order by id) seq
from yourtable t
) d
unpivot
(
value
for col in (address, name, code)
) unpiv
) x
pivot
(
max(value)
for col in (' + #cols + ')
) p '
execute(#query);
See SQL Fiddle with Demo. All versions will give a result:
| ID | UID | NAME1 | CODE1 | ADDRESS1 | NAME2 | CODE2 | ADDRESS2 |
---------------------------------------------------------------------
| 10 | 5 | Jac | 683501 | A | Joe | 727272 | B |
| 13 | 6 | mat | 373737 | C | (null) | (null) | (null) |
Try inner join.
select u2.firstname,u2.col2,
from users u
inner join users u2 on u.userid=u2.userid
where u.firstname=u2.lastname