Using the following tables,
Productivity:
PRODUCTIVITYID PDATE EMPLOYEEID ROOMID ROOMS_SOLD SCR
81 03/26/2016 7499 21 56 43
82 03/26/2016 7566 42 - -
102 03/26/2016 7499 22 434 22
101 03/26/2016 7566 21 43 53
ProductivityD:
PRODUCTIVITYID WORKHRS MEALPANELTY DESCRIPTION
2 50 4 -
21 6.4 1 -
102 6 - -
81 1.32 - -
101 3.6 - -
Rooms:
ID ROOM PROPERTCODE
22 102 6325
41 103 6325
42 104 6325
43 105 6325
EMP:
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
7566 JONES MANAGER 7839 04/02/1981 2975 - 20
7788 SCOTT ANALYST 7566 12/09/1982 3000 - 20
7902 FORD ANALYST 7566 12/03/1981 3000 - 20
7369 SMITH CLERK 7902 12/17/1980 800 - 20
7499 ALLEN SALESMAN 7698 02/20/1981 1600 300 30
The following query is generating below output but I need to group employees and sum workhrs and then pivot RM_ROOM and RM_SCR
WITH pivot_data AS (
SELECT eNAME,workhrs,room, 'RM' as RM,SCR from PRODUCTIVITY p,PRODUCTIVITYd d, emp e, ROOMS R
where p.PRODUCTIVITYID=d.PRODUCTIVITYID and e.empno=p.employeeid
AND R.ID=P.ROOMID
)
SELECT *
FROM pivot_data
PIVOT (
MIN(room) as room,min(scr) as SCR --<-- pivot_clause
FOR RM--<-- pivot_for_clause
IN ('RM') --<-- pivot_in_clause
)
Current Output:
ENAME WORKHRS 'RM'_ROOM 'RM'_SCR
JONES 3.6 101 53
ALLEN 6 102 22
ALLEN 1.32 101 43
Desired Output:
ENAME WORKHRS 'RM'_ROOM 'RM'_SCR 'RM'_ROOM 'RM'_SCR
JONES 3.6 101 53 - -
ALLEN 7.32 101 43 102 22
You are pivoting on a fixed value, the string literal 'RM', so you're really not doing anything useful in the pivot - the output is the same as you'd get from running the 'pivot_data' query on its own:
SELECT eNAME,workhrs,room, SCR from PRODUCTIVITY p,PRODUCTIVITYd d, emp e, ROOMS R
where p.PRODUCTIVITYID=d.PRODUCTIVITYID and e.empno=p.employeeid
AND R.ID=P.ROOMID;
ENAME WORKHRS ROOM SCR
----- ---------- ---------- ----------
JONES 3.6 101 53
ALLEN 1.32 101 43
ALLEN 6 102 22
You want the aggregate workhrs for each employee, and a pivot of the rooms they sold. If you change that query to get the analytic sum of workhrs and a ranking of the room/scr values (and using modern join syntax) you get:
select e.ename, r.room, p.scr,
sum(d.workhrs) over (partition by e.ename) as wrkhrs,
rank() over (partition by e.ename order by r.room, p.scr) as rnk
from productivity p
join productivityd d on d.productivityid = p.productivityid
join emp e on e.empno=p.employeeid
join rooms r on r.id = p.roomid;
ENAME ROOM SCR WRKHRS RNK
----- ---------- ---------- ---------- ----------
ALLEN 101 43 7.32 1
ALLEN 102 22 7.32 2
JONES 101 53 3.6 1
You can then pivot on that generated rnk number:
with pivot_data as (
select e.ename, r.room, p.scr,
sum(d.workhrs) over (partition by e.ename) as wrkhrs,
rank() over (partition by e.ename order by r.room, p.scr) as rnk
from productivity p
join productivityd d on d.productivityid = p.productivityid
join emp e on e.empno=p.employeeid
join rooms r on r.id = p.roomid
)
select *
from pivot_data
pivot (
min(room) as room, min(scr) as scr --<-- pivot_clause
for rnk --<-- pivot_for_clause
in (1, 2, 3) --<-- pivot_in_clause
);
ENAME WRKHRS 1_ROOM 1_SCR 2_ROOM 2_SCR 3_ROOM 3_SCR
----- ---------- ---------- ---------- ---------- ---------- ---------- ----------
ALLEN 7.32 101 43 102 22
JONES 3.6 101 53
You need to know the maximum number of rooms any employee may have - i.e. the highest rnk could ever be - and include all of those in the in clause. Which means you're likely to end up with empty columns, as in this example where there is no data for 3_room or 3_scr. You can't avoid that though, unless you get an XML result or generate the query dynamically.
What you are saying makes no sense. What do you mean by "pivot RM_ROOM"? So I have to guess. I am guessing you want to group employees and sum workhrs, and then pivot the result. The "Output" you show seems to be the output for pivot_data, your subquery.
Your answer will only have eNAME and for each of them, a count of hours worked. So you don't need to SELECT the room numbers in the pivot_data subquery. You only need eNAME and workhrs. Then it is a simple matter of using the PIVOT syntax:
WITH pivot_data AS (
SELECT eNAME, workhrs FROM PRODUCTIVITY p,PRODUCTIVITYd d, emp e, ROOMS R
where p.PRODUCTIVITYID=d.PRODUCTIVITYID and e.empno=p.employeeid
AND R.ID=P.ROOMID
)
SELECT *
FROM pivot_data
PIVOT (
SUM(workhrs)
FOR eNAME IN ('JONES', 'ALLEN')
)
/
Output:
'JONES' 'ALLEN'
---------- ----------
3.6 7.32
Related
Lets say I have the following 2 datasets:
A B C
----------- ----------- -----------
1 100 1000
1 120 1001
2 140 1002
2 160 1003
3 180 1004
3 190 1005
3 200 1006
and
A D E
----------- ----------- -----------
1 61 2000
1 62 2001
1 63 2001
2 64 2002
3 65 2004
3 66 2005
3 67 2006
3 68 2006
Is it possible to generate the following output dataset (. represents null)?
A B C D E
----------- ----------- ----------- ----------- -----------
1 100 1000 61 2000
1 120 1001 62 2001
1 . . 63 2001
2 140 1002 64 2002
2 160 1003 . .
3 180 1004 65 2004
3 190 1005 66 2005
3 200 1006 67 2006
3 . . 68 2006
The merge takes all the records from both tables, and adds them to the result set at most once.
If records join they are not multiplied as in a classical sql join. Each record is aligned with a matching record and when they run out of records null is inserted.
I've been thinking that perhaps the new partitioning functions can achieve this, but I've been away from SQL too long now and I cant think of a way to do this "special join".
I've also considered making a distinct list of the keys and then left joining them to the 2 tables, but then I get stuck, because the join will still multiply the record counts..
You can do this with the row_number() windowing function. Naming the two datasets DS1 and DS2 the result will look like this:
WITH DS1Seq As (
SELECT A, B, C, row_number() OVER (partition by A order by A, B, C) As SeqNumber
FROM DS1
),
DS2Seq As (
SELECT A, D, E, row_number() OVER (partition by A order by A, D, E) As SeqNumber
FROM DS2
)
SELECT coalesce(DS1Seq.A, DS2Seq.A) As A, B, C, D, E
FROM DS1Seq
FULL JOIN DS2Seq on DS1Seq.A = DS2Seq.A AND DS1Seq.SeqNumber = DS2Seq.SeqNumber
I have a Employee Wages table like this, with their EmpID and their wages.
EmpId | Wages
================
101 | 1280
102 | 1600
103 | 1400
104 | 1401
105 | 1430
106 | 1300
I need to write a Stored Procedure in SQL Server, to group the Employees according to their wages, such that similar salaried people are in groups together and the deviations within the group is as minimum as possible.
There are no other conditions or rules mentioned.
The output should look like this
EmpId | Wages | Group
=======================
101 | 1280 | 1
106 | 1300 | 1
103 | 1400 | 2
104 | 1401 | 2
105 | 1430 | 2
102 | 1600 | 3
You can use a query like the following:
SELECT EmpId, Wages,
DENSE_RANK() OVER (ORDER BY CAST(Wages - t.min_wage AS INT) / 100) AS grp
FROM mytable
CROSS JOIN (SELECT MIN(Wages) AS min_wage FROM mytable) AS t
The query calculates the distance of each wage from the minimum wage and then uses integer division by 100 in order to place records in slices. So all records that have a deviation that is between 0 - 99 off the minimum wage are placed in the first slice. The second slice contains records off by 100 - 199 from the minimum wage, etc.
You can for +-30 deviation as the below:
DECLARE #Tbl TABLE (EmpId INT, Wages INT)
INSERT INTO #Tbl
VALUES
(99, 99),
(100, 101),
(101, 1280),
(102, 1600),
(103, 1400),
(104, 1401),
(105, 1430),
(106, 1300)
;WITH CTE AS ( SELECT *, ROW_NUMBER() OVER (ORDER BY Wages) AS RowId FROM #Tbl )
SELECT
A.EmpId ,
A.Wages ,
DENSE_RANK() OVER (ORDER BY MIN(B.RowId)) [Group]
FROM
CTE A CROSS JOIN CTE B
WHERE
ABS(B.Wages - A.Wages) BETWEEN 0 AND 30 -- Here +-30
GROUP BY A.EmpId, A.Wages
ORDER BY A.Wages
Result:
EmpId Wages Group
----------- ----------- --------------------
99 99 1
100 101 1
101 1280 2
106 1300 2
103 1400 3
104 1401 3
105 1430 3
102 1600 4
I am trying to retrieve the most recent entry of a record in an Access report. The query I have gives me the results in SQL-Server but row_number is not compatible with Access. Its been suggested that I use the max function in Access. Can you assist me in generating this report?
SELECT cID, CName, Address, Project#, JobOwner, SubStatusID, Status, JNJobID, JNNote
FROM (
SELECT
cID, CName, Address, Project#, JobOwner, SubStatusID, Status, JNJobID, JNNote
, ROW_NUMBER() OVER (PARTITION BY JNJobID ORDER BY JNDate DESC) AS r
FROM [JobNotes]
Left JOIN Jobs ON [JobNotes].JNJobID = Jobs.JobID
Left JOIN Addresses ON Jobs.JobAddressID = Addresses.AddressID
Left JOIN Customers ON Jobs.JobCustomerID = Customers.CID
Left JOIN Status ON Jobs.JobSubStatusID = Status.StatusID
) x
WHERE r = 1 and customerID = 134 and jobsubstatusid <> 14 and jobsubstatusid <> 15 and jobsubstatusid <> 16 and jobsubstatusid <> 42 and jobsubstatusid <>38 and jobsubstatusid <>75
Jobs Table
JobID Project# JobOwner JobStatusID AddressID JobCustomerID
6972 PN1 John 1 333 222
6973 PN2 Sarah 3 444 666
6974 PN3 James 6 555 777
Address Table
AddressID Address
333 1333 Janes Ln
444 5555 Davis Blvd
555 888 Post Rd
Customer Table
CID CName
222 Builder
666 HomeOwner
777 HOA
JobNotes Table
JobNotesID JNJobID JNDate JNNote
11800 6972 2016-03-15 00:00:00.000 Example 1
11874 6972 2016-03-17 00:00:00.000 Example 2
12181 6972 2016-03-25 00:00:00.000 Example 3
12006 6973 2016-03-21 00:00:00.000 Example 4
11961 6974 2016-03-18 00:00:00.000 Example 5
11924 6974 2016-03-17 00:00:00.000 Example 6
JobNotes Table
CID CName Address Project# JobOwner SubStatusID Status JNJobID JNNote
222 Builder 1333 Janes Ln PN1 John 1 Sales 6972 Example 3
666 HomeOwner 5555 Davis Blvd PN2 Sarah 3 Design 6973 Example 4
777 HOA 888 Post Rd PN3 James 6 Construction 6974 Example 6
I am working on a small reporting application. I have two tables
Agent Table Data
AgentID AgentName
------- ---------
1001 ABC
1002 XYZ
1003 POI
1004 JKL
Report Table Data
ReportID AgentId Labor Mandays Amount SubmitDate
-------- ------- ----- ------- ------ ----------
1 1001 30 10 5000 11/12/2011
2 1001 44 18 8000 11/14/2011
3 1002 33 75 3022 11/12/2011
4 1001 10 10 1500 11/14/2011
5 1002 10 10 1800 11/14/2011
6 1001 10 10 1400 11/14/2011
7 1003 40 40 1500 11/14/2011
8 1003 40 40 1800 11/14/2011
I want to generate a report which gives us output like
ReportID AgentId Labor Mandays Amount SubmitDate
-------- ------- ----- ------- ------ ----------
1 1001 30 10 5000 11/12/2011
3 1002 33 75 3022 11/12/2011
6 1001 10 10 1400 11/14/2011
5 1002 10 10 1800 11/14/2011
8 1003 40 40 1800 11/14/2011
Thanks in Advance
You didn't mention what VERSION of SQL Server you're using - if you're on 2005 or newer, you can use a CTE (Common Table Expression) with the ROW_NUMBER function:
;WITH LastPerAgent AS
(
SELECT
AgentID, ReportID, Labor, Mandays, Amount, SubmitDate,
ROW_NUMBER() OVER(PARTITION BY AgentID,SubmitDate
ORDER BY ReportID DESC) AS 'RowNum'
FROM dbo.Report
)
SELECT
AgentID, ReportID, Labor, Mandays, Amount, SubmitDate,
FROM LastPerAgent
WHERE RowNum = 1
This CTE "partitions" your data by AgentID and SubmitDate, and for each partition, the ROW_NUMBER function hands out sequential numbers, starting at 1 and ordered by ReportID DESC - so the "last" row (with the highest ReportID) for each (AgentID, SubmitDate) pair gets RowNum = 1 which is what I select from the CTE in the SELECT statement after it.
PS: this doesn't work 100% on your input data, since you've not defined how to group and how to eliminate rows.... you might need to adapt this query a bit, based on your requirements...
Is the mechanism used in multiple insertion of rows in the table through single query is same as that of inserting single row with a single query.If not what is the exact mechanism?
If you are fetching the data from an existing table, you can use INSERT INTO TABLE3 ( SELECT * FROM TABLE1 UNION SELECT * FROM TABLE2 ....) to fetch and insert the data in a single go.
SQL> SELECT * FROM SCOTT.EMP WHERE JOB = 'ANALYST1';
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO GENDER
---------- ---------- --------- ---------- --------- ---------- ---------- ---------- --------
8909 LUTHAR ANALYST1 7698 22-JUL-99 1232 788 50 F
8999 AMAN ANALYST1 7698 22-JUL-99 8569 788 50 M
7788 SCOTT ANALYST1 7566 19-APR-87 3000 150 M
7902 2 ANALYST1 7566 03-DEC-81 3000 M
SQL> SELECT * FROM SCOTT.EMP WHERE JOB = 'MANAGER';
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO GENDER
---------- ---------- --------- ---------- --------- ---------- ---------- ---------- ---------
7566 5 MANAGER 7839 02-APR-81 2975 150 F
7698 10000 MANAGER 7839 01-MAY-81 2850 150 F
7782 CLARK MANAGER 7839 09-JUN-81 2450 150 F
SQL> CREATE TABLE EMP1 AS ( SELECT * FROM SCOTT.EMP WHERE 1=2);
Table created.
SQL> SELECT * FROM EMP1;
no rows selected
SQL> INSERT INTO EMP1 ( SELECT * FROM SCOTT.EMP WHERE JOB = 'ANALYST1'
2 UNION
3 SELECT * FROM SCOTT.EMP WHERE JOB = 'MANAGER');
7 rows created.
SQL> SELECT * FROM EMP1;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO GENDER
---------- ---------- --------- ---------- --------- ---------- ---------- ---------- --------
7566 5 MANAGER 7839 02-APR-81 2975 150 F
7698 10000 MANAGER 7839 01-MAY-81 2850 150 F
7782 CLARK MANAGER 7839 09-JUN-81 2450 150 F
7788 SCOTT ANALYST1 7566 19-APR-87 3000 150 M
7902 2 ANALYST1 7566 03-DEC-81 3000 M
8909 LUTHAR ANALYST1 7698 22-JUL-99 1232 788 50 F
8999 AMAN ANALYST1 7698 22-JUL-99 8569 788 50 M
7 rows selected.
You can chain many SQL statements in a single Oracle query by separating each SQL command with a semicolon. For instance:
INSERT INTO table VALUES(1,2); INSERT INTO table VALUES(3,4);