Finding missing records in joined tables using ID and Dates in SQL? - sql-server

I'm having trouble writing a check that will find missing records.
My tables are Provider, ProviderRelationship, Agreement, and AgreementProvider. I need to find missing records where the Provider has a ProviderRelationship, but no record for AgreementProvider for that specific time frame.
Both ProviderRelationship and AgreementProvider have StartDate and EndDate fields. Providers can only have 1 current ProviderRelationship, but they can have multiple ProviderRelationship entries. For example, ProviderA has ProviderRelationship1 on 3/17/2019, then ProviderRelationship2 from 3/18/2019 to 6/30/2020 and AgreementProvider2 for the same dates.
The closest I've come is this, which gets Providers that have never had any AgreementProvider entries at all. When I add in the date validation, it also includes the previous ProviderRelationships.
CREATE TABLE dbo.Provider
ProviderID INT IDENTITY(1,1) NOT NULL,
Name VARCHAR(200) NULL
ProviderID Name
9003055 ABC
6102 DEF
2743 Desired
9999 Ideal
CREATE TABLE dbo.ProviderRelationship
ProviderRelationshipID INT IDENTITY(1,1) NOT NULL,
ParentProviderID INT NOT NULL,
ProviderID INT NOT NULL,
StartDate DATE NOT NULL,
EndDate DATE NOT NULL
ProviderRelationshipID ParentProviderID ProviderID StartDate EndDate
1 9003055 9003055 2017-03-01 2017-03-27 --providers who are the parent provider should not appear
2 1021 9003055 2017-03-27 2017-03-27
3 1021 9003055 2017-03-28 2100-01-01 --should not appear
4 184 6102 2015-07-01 2015-07-01
5 6102 6102 2015-07-02 2100-01-01
6 244 2743 2015-07-01 2100-01-01 --there is no AgreementProviderID record for this one
7 1234 9999 2018-08-01 2019-09-01
8 4321 9999 2019-10-01 2100-01-01 --this is the ideal result, since there is a gap in AgreementProvider records
CREATE TABLE dbo.Agreement
AgreementID INT IDENTITY(1,1) NOT NULL
ProviderID INT NULL, --this is the ParentProviderID
RefRegionID INT NULL
AgreementID ProviderID
1 1021
2 1021
3 184
4 184
5 184
6 184
7 244
8 244
9 244
10 1234
11 4321
CREATE TABLE dbo.AgreementProvider
AgreementProviderID INT IDENTITY(1,1) NOT NULL
AgreementID INT NULL,
ProviderID INT NULL,
StartDate DATE NULL,
EndDate DATE NULL
AgreementProviderID AgreementID ProviderID StartDate EndDate
1 1 9003055 2017-03-28 2020-06-30
2 2 9003055 2017-03-28 2020-06-30
3 10 1234 2018-08-01 2020-06-30
SELECT DISTINCT o.*
FROM Provider p
JOIN dbo.ProviderRelationship prel ON prel.ProviderID = p.ProviderID
JOIN dbo.Agreement a ON a.ProviderID = prel.ParentProviderId
JOIN dbo.AgreementProvider ap ON ap.AgreementID = a.AgreementID
WHERE prel.ProviderID <> prel.ParentProviderId
AND p.ProviderID NOT IN (SELECT ap2.ProviderID FROM dbo.AgreementProvider ap2
JOIN dbo.ProviderRelationship prel2 ON prel2.ProviderID = ap2.ProviderID
WHERE prel.ParentProviderId = prel2.ParentProviderId)
It should not compare the date to a previous date, so ProviderID 9003055 should not appear. It should also not consider ProviderID when the ParentProviderID matches. It should get both Desired and Ideal ProviderIDs: Desired does not have a record, and Ideal does not have a current record for the current ProviderRelationship.
How would I rewrite this to include the specific time frame for each ProviderRelationship compared to the AgreementProvider information for those dates?
Ideal output would be this:
ProviderID ParentProviderID RelationshipStartDate RelationshipEndDate
2743 244 2015-07-01 2100-01-01
9999 4321 2019-10-01 2100-01-01
Maybe it's already doing what I want, because there are invalid entries for the ProviderIDs that I don't want included. Maybe this is good enough, I will have to confirm.

Related

is there any unique SQL function to retrieve only year and month from the timestamp and based on that result i need to find out the earliest made

I TRIED THE BELOW MENTIONED CODE BUT IT GIVING DIFFERENT ANSWERS
CODE USED
$SELECT MIN(DATENAME(MM,orders.occurred_at)) AS 'Month',
MIN(DATENAME(YY,orders.occurred_at)) AS 'Year'
FROM orders
TABLE
id account_id occurred_at
1 1001 2015-10-06 17:31:14.000
2 1001 2015-11-05 03:34:33.000
3 1001 2015-12-04 04:21:55.000
4 1001 2016-01-02 01:18:24.000
5 1001 2016-02-01 19:27:27.000
6 1001 2016-03-02 15:29:32.000
7 1001 2016-04-01 11:20:18.000
8 1001 2016-05-01 15:55:51.000
9 1001 2016-05-31 21:22:48.000
10 1001 2016-06-30 12:32:05.000
11 1001 2016-07-30 03:26:30.000
12 1001 2016-08-28 07:13:39.000
13 1001 2016-09-26 23:28:25.000
14 1001 2016-10-26 20:31:30.000
15 1001 2016-11-25 23:21:32.000
16 1001 2016-12-24 05:53:13.000
17 1011 2016-12-21 10:59:34.000
18 1021 2015-10-12 02:21:56.000
19 1021 2015-11-11 07:37:01.000
20 1021 2015-12-11 16:53:18.000
I didn't understand if you want to see all of the results and just sort them so that the earliest instance would show up first in the list, or if you only want the earliest record by itself.
Sorting the list by the timestamp and displaying the Month() and Year() from the timestamp is easy enough:
SELECT
ID
,Account_ID
,Occurred_At
,MONTH(Occurred_At) AS Occurred_Month
,YEAR(Occurred_At) AS Occurred_Year
FROM Orders
ORDER BY Occurred_At ASC -- This puts the oldest record at the top of the list
;
If you only want to retrieve only the oldest record, then you could do this:
SELECT TOP 1 -- This returns only the topmost record
ID
,Account_ID
,Occurred_At
,MONTH(Occurred_At) AS Occurred_Month
,YEAR(Occurred_At) AS Occurred_Year
FROM Orders
ORDER BY Occurred_At ASC -- This puts the oldest record at the top of the list
;
Presumably, you don't need to extract the month and year from Occurred_At in order to know which order was first... The timestamp gives you that information. But if, for some reason, you want to sort by the month and year instead of the full timestamp:
SELECT TOP 1
ID
,Account_ID
--,Occurred_At -- If you don't need this line, you can remove it...
,MONTH(Occurred_At) AS Occurred_Month
,YEAR(Occurred_At) AS Occurred_Year
FROM Orders
ORDER BY Occurred_Year ASC, Occurred_Month ASC
;
Try,
SELECT MONTH(occurred_at) AS Month,
YEAR(occurred_at) AS Year
FROM orders
OR
SELECT FORMAT(occurred_at, 'MMMM') AS Month,
FORMAT(occurred_at, 'yyyy') AS Year
FROM orders
OR
SELECT
FORMAT(occurred_at, 'y') AS 'Month and Year',
FROM orders

SQL Server : finding gaps in employment - island and gap problem

I have been going through stack overflow to try and work this out over the last week and I still can't work out a viable solution so was wondering if anyone could offer me some help/advice?
Explanation of the data structures
I have the following tables:
Position table (zz_position) which is used to hold the details of the
position (Job ID) include the date range that it is valid for.
PosNo Description Date_From Date_To
---------------------------------------------------------
10001 System Administrator 20170101 20231231
Resource table (zz_resource) which is used to hold the details of a resource (employee) including the date that they joined the company and left it
resID description date_from date_to
------------------------------------------
100 Sam 20160101 20991231
101 Joe 20150101 20991231
Employment table (zz_employment) which is used to link position to resources within a date from and to range
PosNo resID Date_From Date_To seqNo
---------------------------------------------------
10001 100 20180101 20180401 1
10001 101 20180601 20191231 2
10001 100 20200101 20991231 3
Problem
Now due to people changing positions, a post might not be filled for a period of time and what I am trying to do is produce a report that I can use to give me a breakdown of the status of a post at any point in time.
I know that I can produce one which fully maps each day using a calendar table however what I want is a report which produces the data in the following aggregated format:
PosNo resID Date_From Date_To seqNo
-------------------------------------------------
10001 NULL 20170101 20171231 0
10001 100 20180101 20180401 1
10001 NULL 20180402 20180530 0
10001 101 20180601 20191231 2
10001 100 20200101 20231231 3
insert into zz_employment
values ('10001', '100', '2018-01-01 00:00:00.000', '2018-04-01 00:00:00.000', 1),
('10001', '101', '2018-06-01 00:00:00.000', '2019-12-31 00:00:00.000', 2),
('10001', '100', '2020-01-01 00:00:00.000', '2099-12-31 00:00:00.000', 3)
(note how the report has taken the two lines in the table and produced a fully speced out life of the employment where the first null line date from is pulled from the position start date and the last line date to is pulled from the position end date.
Ideally I would like this as a view/function however due to the complexity I would be more than happy to have a series of T SQL statements that I can run each night as part of a data warehouse routine.
Rules
all dates are truncated to datetime so that an date_to is referencing the date that it ends not the date and time that it ends
if the post/employment/resource has no end date then it will be denoted as 20991231
if the employment itself is open ended then the date to in the employment table is denoted as 20991231 even through the position itself might end in 20231231. Ideally I would like the result to respect the position end date.
SQL code:
CREATE TABLE zz_position
(
posNo varchar(25) NOT NULL,
description varchar(25) NOT NULL,
date_from datetime NULL,
date_to datetime NULL
)
insert into zz_position
values ('10001', 'System Administrator', '2017-01-01 00:00:00.000', '2020-12-31 00:00:00.000')
go
CREATE TABLE zz_resource
(
resID varchar(25) NOT NULL,
description varchar(25) NOT NULL,
date_from datetime NULL,
date_to datetime NULL
)
insert into zz_resource
values ('100', 'Sam', '2016-01-01 00:00:00.000', '2099-12-31 00:00:00.000'),
('101', 'Joe', '2015-01-01 00:00:00.000', '2099-12-31 00:00:00.000')
go
CREATE TABLE zz_employment
(
posNo varchar(25) NOT NULL,
resID varchar(25) NOT NULL,
date_from datetime NULL,
date_to datetime NULL,
seqNo int NULL
)
insert into zz_employment
values ('10001', '100', '2018-01-01 00:00:00.000', '2018-04-01 00:00:00.000', 1),
('10001', '101', '2018-06-01 00:00:00.000', '2019-12-31 00:00:00.000', 2),
('10001', '100', '2020-01-01 00:00:00.000', '2099-12-31 00:00:00.000', 3)
There are 2 caveats for this problem:
A calendar table.
A way to correctly group unemployed periods when there's an employed period in between.
The following solution uses a calendar table (SQL included) and an DATEDIFF() with anchor-date trick to group correctly for the 2nd point.
Complete DB Fiddle here.
Solution (explanation below):
;WITH AllPositionDates AS
(
SELECT
T.posNo,
C.GeneratedDate
FROM
zz_position AS T
INNER JOIN Calendar AS C ON C.GeneratedDate BETWEEN T.date_from AND T.date_to
),
AllEmployedDates AS
(
SELECT
T.posNo,
T.resID,
T.seqNo,
C.GeneratedDate
FROM
zz_employment AS T
INNER JOIN Calendar AS C ON C.GeneratedDate BETWEEN T.date_from AND T.date_to
),
PositionsByEmployed AS
(
SELECT
P.posNo,
P.GeneratedDate,
E.resID,
E.seqNo,
NullRowNumber = ROW_NUMBER() OVER (
PARTITION BY
P.posNo,
CASE WHEN E.posNo IS NULL THEN 1 ELSE 2 END
ORDER BY
P.GeneratedDate ASC)
FROM
AllPositionDates AS P
LEFT JOIN AllEmployedDates AS E ON
P.posNo = E.posNo AND
P.GeneratedDate = E.GeneratedDate
)
SELECT
P.posNo,
P.resID,
Date_From = MIN(P.GeneratedDate),
Date_To = MAX(P.GeneratedDate),
seqNo = ISNULL(P.seqNo, 0)
FROM
PositionsByEmployed AS P
GROUP BY
P.posNo,
P.resID,
P.seqNo,
CASE WHEN P.resId IS NULL THEN P.NullRowNumber - DATEDIFF(DAY, '2000-01-01', P.GeneratedDate) END -- GroupingValueGroupingValue
ORDER BY
P.posNo,
Date_From,
Date_To
The result:
posNo resID Date_From Date_To seqNo
10001 NULL 2017-01-01 2017-12-31 0
10001 100 2018-01-01 2018-04-01 1
10001 NULL 2018-04-02 2018-05-31 0
10001 101 2018-06-01 2019-12-31 2
10001 100 2020-01-01 2020-12-31 3
Explanation
First the creating of a calendar table. This holds 1 row for each day and in this example it's limited to the first and last possible day of the job positions:
DECLARE #DateStart DATE = (SELECT MIN(P.date_from) FROM zz_position AS P)
DECLARE #DateEnd DATE = (SELECT(MAX(P.date_to)) FROM zz_position AS P)
;WITH GeneratedDates AS
(
SELECT
GeneratedDate = #DateStart
UNION ALL
SELECT
GeneratedDate = DATEADD(DAY, 1, G.GeneratedDate)
FROM
GeneratedDates AS G
WHERE
DATEADD(DAY, 1, G.GeneratedDate) <= #DateEnd
)
SELECT
DateID = IDENTITY(INT, 1, 1),
G.GeneratedDate
INTO
Calendar
FROM
GeneratedDates AS G
OPTION
(MAXRECURSION 0)
This generates the following (up to 2020-12-31, which is max date from sample data):
DateID GeneratedDate
1 2017-01-01
2 2017-01-02
3 2017-01-03
4 2017-01-04
5 2017-01-05
6 2017-01-06
7 2017-01-07
Now we use a join with a between to "spread" the periods of both the positions and the employees periods (on different CTEs), so we get 1 row for each day, for each position/employee.
-- AllPositionDates
SELECT
T.posNo,
C.GeneratedDate
FROM
zz_position AS T
INNER JOIN Calendar AS C ON C.GeneratedDate BETWEEN T.date_from AND T.date_to
-- AllEmployedDates
SELECT
T.posNo,
T.resID,
T.seqNo,
C.GeneratedDate
FROM
zz_employment AS T
INNER JOIN Calendar AS C ON C.GeneratedDate BETWEEN T.date_from AND T.date_to
With these, we join them together by position and date using LEFT JOIN, so we get all days of each position and the matching employee (if exists). We also calculate a row number for all NULL values for each position that we are gonna use later. Note that this row number increases 1 by 1 with each following date accordingly.
;WITH AllPositionDates AS
(
SELECT
T.posNo,
C.GeneratedDate
FROM
zz_position AS T
INNER JOIN Calendar AS C ON C.GeneratedDate BETWEEN T.date_from AND T.date_to
),
AllEmployedDates AS
(
SELECT
T.posNo,
T.resID,
T.seqNo,
C.GeneratedDate
FROM
zz_employment AS T
INNER JOIN Calendar AS C ON C.GeneratedDate BETWEEN T.date_from AND T.date_to
)
-- PositionsByEmployee
SELECT
P.posNo,
P.GeneratedDate,
E.resID,
E.seqNo,
NullRowNumber = ROW_NUMBER() OVER (
PARTITION BY
P.posNo,
CASE WHEN E.posNo IS NULL THEN 1 ELSE 2 END
ORDER BY
P.GeneratedDate ASC)
FROM
AllPositionDates AS P
LEFT JOIN AllEmployedDates AS E ON
P.posNo = E.posNo AND
P.GeneratedDate = E.GeneratedDate
Now with the tricky part. If we calculate the amount of days of difference between a hard-coded date and each day, we get a similar "row number" that increases consistently for each date.
SELECT
P.posNo,
P.GeneratedDate,
DateDiff = DATEDIFF(DAY, '2000-01-01', P.GeneratedDate),
P.NullRowNumber
FROM
PositionsByEmployed AS P -- This is declare with the WITH (full solution below)
ORDER BY
P.posNo,
P.GeneratedDate
We get the following:
posNo GeneratedDate DateDiff NullRowNumber
10001 2017-01-01 6210 1
10001 2017-01-02 6211 2
10001 2017-01-03 6212 3
10001 2017-01-04 6213 4
10001 2017-01-05 6214 5
10001 2017-01-06 6215 6
10001 2017-01-07 6216 7
10001 2017-01-08 6217 8
10001 2017-01-09 6218 9
If we add another column with the rest of these 2 you will see that the value remains the same:
SELECT
P.posNo,
P.GeneratedDate,
DateDiff = DATEDIFF(DAY, '2000-01-01', P.GeneratedDate),
P.NullRowNumber,
GroupingValue = P.NullRowNumber - DATEDIFF(DAY, '2000-01-01', P.GeneratedDate)
FROM
PositionsByEmployed AS P
ORDER BY
P.posNo,
P.GeneratedDate
We get:
posNo GeneratedDate DateDiff NullRowNumber GroupingValue
10001 2017-01-01 6210 1 -6209
10001 2017-01-02 6211 2 -6209
10001 2017-01-03 6212 3 -6209
10001 2017-01-04 6213 4 -6209
10001 2017-01-05 6214 5 -6209
10001 2017-01-06 6215 6 -6209
10001 2017-01-07 6216 7 -6209
10001 2017-01-08 6217 8 -6209
10001 2017-01-09 6218 9 -6209
10001 2017-01-10 6219 10 -6209
But if we scroll down until we see values that are NULL for employee (from the ROW_NUMBER() PARTITION BY expression E.PosNo), we see that the rest differs, since the ROW_NUMBER() kept increasing 1 by 1 and the DATEDIFF jumped because there are employed people in between:
posNo GeneratedDate DateDiff NullRowNumber GroupingValue
10001 2017-12-28 6571 362 -6209
10001 2017-12-29 6572 363 -6209
10001 2017-12-30 6573 364 -6209
10001 2017-12-31 6574 365 -6209
...
10001 2018-04-02 6666 366 -6300
10001 2018-04-03 6667 367 -6300
10001 2018-04-04 6668 368 -6300
10001 2018-04-05 6669 369 -6300
10001 2018-04-06 6670 370 -6300
10001 2018-04-07 6671 371 -6300
Use use this "GroupingValue" as an additional GROUP BY to correctly separate position intervals that fall outside employed intervals.

Compare (greater than or less than) 1st row value with 2nd row value based on Paid date for the Key, and then check if 3rd row is greater than 2nd

Current result
-- Scenario1--For ID 1 and 2, I want to compare if Diff Between (DD,Begin date of ID 2 , End Date OF ID 1) < = 1, then the value should be true and be displayed other wise don't display the value
Scenario2 For ID 10 and 11 -- Begin Date for ID 10 and Begin Date of ID 11 is same, so I want to compare based on Paid date, As Paid Date of ID 11 is > Paid date of ID 10. I want the value to be displayed which is greater
Id Employeekey ReceiptNo BeginDate endDate PaidDate main Supplier RollNo
1 101 5505 3/28/2016 3/29/2016 4/29/2016 1 2001 655
2 101 5506 3/30/2016 4/1/2016 4/30/2016 1 2001 666
3 101 5507 4/5/2016 4/6/2016 4/30/2016 1 2001 155
4 101 5508 4/7/2016 4/10/2016 5/1/2016 1 2001 155
5 101 5509 4/11/2016 4/14/2016 5/5/2016 1 2001 155
6 101 5510 5/1/2016 5/3/2016 6/24/2016 1 2001 255
7 101 5511 5/1/2016 5/3/2016 6/30/2016 1 2001 265
8 102 5512 3/28/2017 3/29/2016 4/29/2017 1 2001 655
9 102 5513 3/28/2017 3/29/2016 4/29/2017 1 2001 655
10 102 5514 3/28/2017 3/29/2016 4/29/2017 1 2001 655
11 102 5515 3/28/2016 3/29/2016 5/29/2016 1 2001 655
12 102 5515 3/28/2016 3/29/2016 5/29/2016 1 2001 659
Look at the dates for your second scenario. The dates aren't the same for BeginDate for ID 10 and ID 11. Also, the PaidDate is not >, it's < (look at the years in both cases). With that being said, i edited your test data. Here is how you would do this. You can alter it to fit your needs but this will get you going.
declare #table table (id int,
Employeekey int,
RecieptNo int,
BeginDate datetime,
endDate datetime,
PaidDate datetime,
main int,
Supplier int,
RollNo int)
insert into #table
values
(1,101,5505,'3/28/2016','3/29/2016','4/29/2016',1,2001,655),
(2,101,5506,'3/30/2016','4/1/2016','4/30/2016',1,2001,666),
(3,101,5507,'4/5/2016','4/6/2016','4/30/2016',1,2001,155),
(4,101,5508,'4/7/2016','4/10/2016','5/1/2016',1,2001,155),
(5,101,5509,'4/11/2016','4/14/2016','5/5/2016',1,2001,155),
(6,101,5510,'5/1/2016','5/3/2016','6/24/2016',1,2001,255),
(7,101,5511,'5/1/2016','5/3/2016','6/30/2016',1,2001,265),
(8,102,5512,'3/28/2017','3/29/2016','4/29/2017',1,2001,655),
(9,102,5513,'3/28/2017','3/29/2016','4/29/2017',1,2001,655),
(10,102,5514,'3/28/2016','3/29/2016','4/29/2016',1,2001,655), --changed this to 2016 for being date and paid date
(11,102,5515,'3/28/2016','3/29/2016','5/29/2017',1,2001,655), --changed this to 2017 for paid date
(12,102,5515,'3/28/2016','3/29/2016','5/29/2016',1,2001,659)
select
*
--scenario 1
,case
when datediff(day,lead(BeginDate) over (partition by EmployeeKey order by Id),endDate) <=1 then 'True'
end
--scenario 2... id 10 and 11 have different years in your test but i fixed this in my test
,case
when lead(BeginDate) over (partition by EmployeeKey order by Id) = BeginDate
and lead(PaidDate) over (partition by EmployeeKey order by Id) > PaidDate then 'True'
end
from #table

Get last row by datetime in SQL Server

I have below SQL table:
Id | Code | DateTime1 | DateTime2
1 3AA2 2017-02-01 14:23:00.000 2017-02-01 20:00:00.000
2 E323 2017-02-12 17:34:34.032 2017-02-12 18:34:34.032
3 DFG3 2017-03-08 09:20:10.032 2017-03-08 12:30:10.032
4 LKF0 2017-04-24 11:14:00.000 2017-04-24 13:40:00.000
5 DFG3 2017-04-20 13:34:42.132 2017-04-20 15:12:12.132
6 DFG3 2017-04-20 13:34:42.132 NULL
Id is an auto numeric field.
Code is string and Datetime1 and DateTime2 are datetime type. Also DateTime1 cannot be null but datetime2 can be.
I would like to obtain the last row by datetime1 (MAX datetime1, most recent one) that match a concrete code and it has datetime2 set to NULL.
For example, taken into account above table, for code DFG3 I would like to obtain row with Id=6, its max date for datetime1, that is "2017-04-20 13:34:42.132"
But now imagine the following case:
Id | Code | DateTime1 | DateTime2
1 3AA2 2017-02-01 14:23:00.000 2017-02-01 20:00:00.000
2 E323 2017-02-12 17:34:34.032 2017-02-12 18:34:34.032
3 DFG3 2017-03-08 09:20:10.032 2017-03-08 12:30:10.032
4 LKF0 2017-04-24 11:14:00.000 2017-04-24 13:40:00.000
5 DFG3 2017-04-20 13:34:42.132 NULL
6 DFG3 2017-05-02 16:34:34.032 2017-05-02 21:00:00.032
Again, taken into account above table, I would like to obtain the same, that is, the last row by datetime1 (Max datetime1, most recent one) that match a concrete code and it has datetime2 set to NULL.
Then, in this last case for code DFG3 no rows must be return because row with Id=6 is the last by datetime1 (most recent) for code DFG3 but is not NULL.
How can I do this?
Can you try this query and let me know if it works for your case
Select * From [TableName] where [Code]='DFG3' and [datetime2] is null and [datetime1] = (select max([datetime1]) from [TableName] where [Code]='DFG3')
This bring you all the latest code on your table, then you select only the one with datetime2 is null
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Code
ORDER BY DateTime1 Desc) as rn
FROM yourTable
) as T
WHERE rn = 1 -- The row with latest date for each code will have 1
and dateTime2 IS NULL
and code = 'DFG3' -- OPTIONAL

SQL Server : pivot or not to pivot

I have a table in which I run a select statement and returns the following data
Name date skill seconds calls
----------------------------------------------
bob 9/2/2016 706 12771 56
bob 9/2/2016 707 4061 16
bob 9/2/2016 708 2577 15
bob 9/2/2016 709 2156 6
I want to return like below one row of data:
Name date 706sec 706call 707sec 707call 708sec 708call 709sec 709call
----------------------------------------------------------------------------------
bob 9/2/2016 12771 56 4061 16 2577 15 2156 6
My first attempt was a pivot but does not return a single row:
Select
name, date, seconds, calls, [706], [707],[708],[709],
from
(Select
name, date, skill, seconds, calls
From
tablecalls
Where
date between '09/02/2016 00:00' and '09/02/2016 23:59'
and name = 'bob') as b
pivot
(sum(seconds) for skill in ([706], [707], [708], [709] )) as p1
This returns:
name date calls 706sec 707sec 708sec 709sec
---------------------------------------------------------
bob 9/2/2016 6 NULL NULL NULL 2156
bob 9/2/2016 15 NULL NULL 2577 NULL
bob 9/2/2016 16 NULL 4061 NULL NULL
bob 9/2/2016 56 12771 NULL NULL NULL
Maybe PIVOT is not the right way to do this. Is there another way?
can you format your data in question. I am not sure whether I get youe point?
CREATE TABLE #tt([name] VARCHAR(10),[date] DATE,skill INT,seconds INT, calls int )
INSERT INTO #tt
SELECT 'bob','9/2/2016',706,12771,56 UNION all
SELECT 'bob','9/2/2016',707,4061,16 UNION all
SELECT 'bob','9/2/2016',708,2577,15 UNION all
SELECT 'bob','9/2/2016',709,2156,6
SELECT * FROM (
SELECT t.name,t.date,c.* FROM #tt AS t
CROSS APPLY(VALUES(LTRIM(t.skill)+'sec',t.seconds),(LTRIM(t.skill)+'calls',t.seconds)) c(t,v)
) AS A
PIVOT(MAX(v) FOR t IN ([706sec],[706calls],[707sec],[707calls],[708sec],[708calls],[709sec],[709calls])) p
name date 706sec 706calls 707sec 707calls 708sec 708calls 709sec 709calls
---------- ---------- ----------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
bob 2016-09-02 12771 12771 4061 4061 2577 2577 2156 2156
if the count of skill is not fix, you can use dynamic script:
DECLARE #col VARCHAR(max),#sql VARCHAR(max)
SELECT #col=ISNULL(#col+',[','[')+LTRIM(skill)+'sec],['+LTRIM(skill)+'calls]' FROM #tt GROUP BY skill
SET #sql='
SELECT * FROM (
SELECT t.name,t.date,c.* FROM #tt AS t
CROSS APPLY(VALUES(LTRIM(t.skill)+''sec'',t.seconds),(LTRIM(t.skill)+''calls'',t.seconds)) c(t,v)
) AS A
PIVOT(MAX(v) FOR t IN ('+#col+')) p'
EXEC (#sql)

Resources