SQL Aggregate Function Query not Producing Expected Results - sql

The following is my query to go through about a million rows to calculate MTBUR (Mean Time Before Unscheduled Repair):
DECLARE #BeginDate date = '01-01-2013',
#EndDate date = '12-31-2013'
BEGIN
SELECT H.AutoType,
COALESCE(((SUM(H.Hours))/(CASE WHEN R.ReceivedDate BETWEEN #BeginDate AND #EndDate THEN COUNT(R.Confirmed) END)), SUM(H.Hours)) AS 'MTBUR'
FROM Hours H
INNER JOIN Repair R
ON H.SN = R.SN
WHERE (R.Confirmed NOT LIKE 'C%' AND R.Confirmed NOT LIKE 'O%')
AND (H.Date BETWEEN #BeginDate AND #EndDate)
GROUP BY H.AutoType,
R.ReceivedDate
END
The following are example results for 2 types:
Type | MTBUR
------------
a | value
a | value
a | value
b | value
b | value
b | value
I want my results to look like this:
Type | MTBUR
------------
a | value
b | value
Why is it grouping the same type several times. I want only 1 value for each type.
Also, Why is the DBMS making me also group by ReceivedDate? I get the feeling that is screwing my results up. Any suggestions?
The following are my CREATE TABLE:
CREATE TABLE [dbo].[acss_hours](
[hoursId] [uniqueidentifier] NOT NULL,
[name] [nvarchar](100) NULL,
[Type] [nvarchar](100) NULL,
[SN] [nvarchar](100) NULL,
[Reg] [nvarchar](100) NULL,
[Hours] [float] NULL,
[Date] [datetime] NULL)
CREATE TABLE [dbo].[repair](
[repairId] [uniqueidentifier] NOT NULL,
[Part] [nvarchar](100) NULL,
[Customer] [nvarchar](100) NULL,
[AutoType] [nvarchar](100) NULL,
[ReceivedDate] [datetime] NULL,
[Confirmed] [nvarchar](100) NULL,
[Company] [nvarchar](100) NULL,
[Reg] [nvarchar](100) NULL,
[Manu] [nvarchar](100) NULL,
[SN] [nvarchar](100) NULL)

You are correct, adding ReceivedDate is screwing up your results. You are getting one row for each type for RecievedDate.
SQL Server if forcing you to add RecievedDate to the group by because you are using it in the select clause. When SQL Server processes each AutoType, what ReceivedDate should it use? It has multiple ReceivedDates per AutoType. Either it needs to use each seperate ReceivedDate by adding it to the group by, or it can use a aggregate function like min or max to select one of the RecievedDates.
How do you want your query to handle it?
I think you should wrap your case in the COUNT.
COUNT(CASE WHEN R.ReceivedDate BETWEEN #BeginDate AND #EndDate
THEN R.Confirmed ELSE 0 END)

You need to include R.ReceivedDate from your calculation in the group by because you're evaluating the column with the between statement. Its the same as including the column in the select. Basically any column in the select line that doesn't have an aggregation function needs to be in the group by.

You will have to make use of the keyword DISTINCT.
So effectively, you can query as such:
DECLARE #BeginDate date = '01-01-2013',
#EndDate date = '12-31-2013'
BEGIN
SELECT DISTINCT H.AutoType,
COALESCE(((SUM(H.Hours))/(CASE WHEN R.ReceivedDate BETWEEN #BeginDate AND #EndDate THEN COUNT(R.Confirmed) END)), SUM(H.Hours)) AS 'MTBUR'
FROM Hours H
INNER JOIN Repair R ON H.SN = R.SN
WHERE (R.Confirmed NOT LIKE 'C%' AND R.Confirmed NOT LIKE 'O%')
AND (H.Date BETWEEN #BeginDate AND #EndDate)
GROUP BY H.AutoType, R.ReceivedDate
END
Hope this helps!!!

Didn't let me post this in the comment. Try an inner query:
SELECT H.AutoType, COALESCE(((SUM(H.Hours))/(SUM(x.CountConfirmed))), SUM(H.Hours)) AS 'MTBUR'
FROM
(SELECT H.AutoType, CASE WHEN R.ReceivedDate BETWEEN #BeginDate AND #EndDate THEN 1 ELSE 0 END AS CountConfirmed
FROM Hours H
INNER JOIN Repair R
ON H.SN = R.SN
WHERE (R.Confirmed NOT LIKE 'C%' AND R.Confirmed NOT LIKE 'O%')
AND (H.Date BETWEEN #BeginDate AND #EndDate)) x
JOIN Hours H
ON H.AutoType = x.AutoType
WHERE (H.Date BETWEEN #BeginDate AND #EndDate)
GROUP BY H.AutoType

Related

SELECT using Correlated Sub Queries and multiple tables

I have two tables (Table1 & Table2). One including a date (tDate) column and an ID (tTillNo) column that I would like to group by. And another with values (pValue) that I would like to sum for each date and ID.
The tables can be joined using the 'tName' and 'pName' columns from each table, however the pName column may have duplicates, which I would like to include in the sum.
For the 3 sub queries I also need to include the where clauses using the 'pCode' column.
And I would like to return the columns shown here
Using the below code however returns the sub queries with incorrect totals as I am using 'MAX(a.tName)' to prevent the grouping of this field. Could anyone suggest another way of preventing this or an alternative way of going about this query?
SELECT
DATENAME(Month,a.tDate) as 'Month Name', DAY(a.tDate) as 'Day',
MONTH(a.tDate) as 'Month', YEAR(a.tDate) as 'Year', a.tTillNo,
BankedCash=(SELECT ISNULL(CAST(SUM(b.pValue) as numeric(12,2)),0)
FROM Table2 b
where MAX(a.tName)=b.pName AND pCode = 'CSH'
or pCode = 'CHQ'),
CardTransactions=(SELECT ISNULL(CAST(SUM(b.pValue) as numeric(12,2)),0)
FROM Table2 b
where MAX(a.tName)=b.pName AND b.pCode = 'CRD'),
BankingTotal=(SELECT ISNULL(CAST(SUM(b.pValue) as numeric(12,2)),0)
FROM Table2 b
where MAX(a.tName)=b.pName AND b.pCode = 'CSH' or
b.pCode = 'CHQ' or b.pCode = 'CRD')
FROM Table1 a
group by YEAR(a.tDate), MONTH (a.tDate), DATENAME(Month,a.tDate),
DAY(a.tDate), a.tTillNo
order by YEAR(a.tDate), MONTH (a.tDate), DATENAME(Month,a.tDate)
Any suggestions or article referrals would be highly appreciated. Many thanks in advance.
CREATE TABLE [dbo].[Table1](
[UniqueID] [int] IDENTITY(1,1) NOT NULL,
[tTillNo] [varchar](4) NULL,
[tName] [varchar](20) NULL,
[tDate] [datetime] NULL)
INSERT INTO Table1 (tTillNo, tName, tDate)
VALUES ('0101', '01010000001', '2018-10-30 00:00:00.000'),
('0101', '01010000002', '2018-10-30 00:00:00.000'),
('0102', '01020000001', '2018-10-30 00:00:00.000'),
('0102', '01020000002', '2018-10-30 00:00:00.000')
CREATE TABLE [dbo].[Table2](
[UniqueID] [int] IDENTITY(1,1) NOT NULL,
[pName] [varchar](20) NULL,
[pCode] [varchar](10) NULL,
[pValue] [decimal](22, 7) NULL)
INSERT INTO Table2 (pName, pCode, pValue)
VALUES ('01010000001', 'CRD', '100.0000000'),
('01010000002', 'CSH', '100.0000000'),
('01020000001', 'CHQ', '100.0000000'),
('01020000002', 'CSH', '100.0000000'),
('01020000002', 'CRD', '100.0000000'),
('01010000001', 'CSH', '100.0000000')
I think you can solve this all with a join.
select DATENAME(Month,a.tDate) as 'Month Name'
, DAY(a.tDate) as 'Day'
, MONTH(a.tDate) as 'Month'
, YEAR(a.tDate) as 'Year'
, a.tTillNo
, BankedCash=SUM(case when pCode in( 'CSH','CHQ') then pvalue else 0 end)
, [Card] = SUM(case when pCode in( 'CRD') then pvalue else 0 end)
,Total = SUM(pvalue)
from TableA a
join TableB b on a.tName=b.pName
group by YEAR(a.tDate)
, MONTH (a.tDate)
, DATENAME(Month,a.tDate)
, DAY(a.tDate)
, a.tTillNo

Select all from primary table, join to others

I am hoping somebody can help me. If I do a standard Inner Join or right join, i get very few results. Would also be great to also know why what I am trying wont work.
I have a primary Table of members. MemberID is common to all tables
Select * from apf_members
MemberID--|--AppsTitle--|--AppsFirstName--|--AppsLastName--
2015 Mrs Naomi Specter
2016 Mr Marisa Watson
2025 Mr Elia Barker
2031 Dr Heth Rowing
2044 Ms Kathryn McKenzie
I also want to attached 3 extra columns that are dynamically built.
--CurrentMember--|--UnrenewedMember--|--LapsedMember--
The queries below will currently return the value only ~IF~ the user has a record. where I need it to return a NULL or an empty string.
#theDate is a variable, ill assign it to Getdate for now.
Declare #theDate date
SET #theDate = GetDate()
Select Description as 'CurrentMember' from apf_finances
where #theDate between StartDate and EndDate
and Status = 'Financial Status'
Select Description as 'UnrenewedMember'
from apf_finances
where #theDate between DATEADD(year, -1, StartDate) and DATEADD(year, -1, EndDate)
and Status = 'Financial Status'
Select Description as 'LapsedMember'
from apf_finances
where #theDate between DATEADD(year, -2, StartDate) and DATEADD(year, -2, EndDate)
and Status = 'Financial Status'
Put together,final result would look like this.
MemberID--|--AppsTitle--|--AppsFirstName--|--AppsLastName--|--CurrentMember--|--UnrenewedMember--|--LapsedMember--
2015 Mrs Naomi Specter f nf nf
2016 Mr Marisa Watson uf NULL nf
2025 Mr Elia Barker NULL NULL NULL
2031 Dr Heth Rowing co exp f
2044 Ms Kathryn McKenzie NULL f NULL
*UPDATE 22/10/2013 *
here is a SQL script that should re-create the tables
CREATE TABLE [dbo].[apf_Members](
[MemberID] [int] IDENTITY(1,1) NOT NULL,
[AppsTitle] [nvarchar](50) NULL,
[AppsFirstName] [nvarchar](50) NULL,
[AppsMiddleName] [nvarchar](50) NULL,
[AppsLastName] [nvarchar](50) NULL,
)
GO
CREATE TABLE [dbo].[apf_Finances](
[ID] [int] IDENTITY(1,1) NOT NULL,
[MemberID] [int] NOT NULL,
[Description] [nvarchar](50) NULL,
[StartDate] [date] NULL,
[EndDate] [date] NULL,
[Status] [nvarchar](50) NULL
)
GO
SET IDENTITY_INSERT dbo.[apf_Members] ON
GO
SET IDENTITY_INSERT dbo.[apf_Finances] ON
GO
INSERT INTO [dbo].[apf_Members]
([MemberID],[AppsTitle],[AppsFirstName],[AppsLastName])
VALUES (2015,'Mrs','Naomi', 'Specter' ),
(2016 , 'Mr' ,'Marisa','Watson' ),
(2025 , 'Mr' ,'Elia','Barker'),
(2031 , 'Dr','Heth','Rowing'),
(2044 , 'Ms','Kathryn','McKenzie');
INSERT INTO [apf_Finances]
([ID], [MemberID], [Description], [StartDate], [EndDate], [Status] )
VALUES
(12381, 2016, 'f' ,'2013-10-15','2014-10-14','Financial Status'),
(12382, 2016, '' ,'2013-10-15','2014-10-14','Donation'),
(12361, 2025, 'f' ,'2013-10-12','2014-10-11','Financial Status'),
(12362, 2025, '' ,'2013-10-12','2014-10-11','Donation'),
(12357, 2031, 'f' ,'2013-10-11','2014-10-10','Financial Status'),
(12358, 2031, '' ,'2013-10-11','2014-10-10','Donation'),
(12379, 2044, 'f' ,'2012-10-21','2013-10-20','Financial Status'),
(12380, 2044, '' ,'2012-10-21','2013-10-20','Donation'),
(12377, 2016, 'f' ,'2012-10-17','2013-10-16','Financial Status'),
(12378, 2016, '' ,'2012-10-17','2013-10-16','Donation'),
(12373, 2025, 'f' ,'2012-10-16','2013-10-15','Financial Status'),
(12374, 2031, '' ,'2011-10-16','2013-10-15','Donation'),
(12375, 2031, 'f' ,'2011-10-16','2013-10-15','Financial Status'),
(12376, 2044, '' ,'2011-10-16','2013-10-15','Donation'),
(12371, 2044, 'f' ,'2011-10-15','2013-10-14','Financial Status');
Here is a Northwind example.
You can "filter" by using derived tables.
And then left join on the derived table(s).
You can write yours, and you'll have 3 derived tables.
derivedCurrentMember
derivedUnrenewedMember
derivedLapsedMember
Put your filtering/logic in each derived table. Then left join on each one.
Here is a generic Northwind example.
My "filtering logic" is whether a discount existed or not. (innerOd1.Discount <=0 and innerOd1.Discount > 0). You may not need the group-by.
Use Northwind
GO
Select ords.OrderID
, ISNULL ( derived1.MyCount , 0) as NoDiscountCount
, ISNULL ( derived2.MyCount , 0) as HasDiscountCount
from dbo.Orders ords
left join
( select innerOd1.OrderID , count (*) as MyCount from dbo.[Order Details] innerOd1 where innerOd1.Discount <=0 group by innerOd1.OrderID) derived1
on ords.OrderID = derived1.OrderID
left join
( select innerOd2.OrderID , count (*) as MyCount from dbo.[Order Details] innerOd2 where innerOd2.Discount > 0 group by innerOd2.OrderID) derived2
on ords.OrderID = derived2.OrderID
order by ords.OrderID
::: APPEND :::
Here is the first one.
You can fill in the rest.
declare #theDate datetime
select #theDate = getdate()
select membs.* , currentMemberDerived.[CurrentMember] from
[dbo].[apf_Members] membs
left join
(
Select MemberID , [Description] as 'CurrentMember' from apf_finances
where #theDate between StartDate and EndDate
and Status = 'Financial Status'
) currentMemberDerived
on membs.MemberID = currentMemberDerived.MemberID

How can I group by time in SQL [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
SQL Query Group By Datetime problem?
I am working on an application with 2 steps.
Scan logs and persist data from them in a database.
Read data from database and visualize the data.
The first step is more or less finished. I try to explain the background and my reguirement with the second step.
Each row in the database consists of some info like logdate, logfilename, LogType, logMessage etc. So I want for example write SQL that summarize a given LogType per day.
This is the columns:
[LogDate] [datetime] NOT NULL,
[Computer] [varchar](50) NOT NULL,
[Type] [varchar](50) NOT NULL,
[FileName] [varchar](100) NOT NULL,
[LineNo] [int] NOT NULL,
[UserName] [varchar](50) NOT NULL,
[Message] [varchar](max) NOT NULL,
I imagine the output could be like this if I want to show all rows with Type=TDBError:
Date Sum
2012-10-01 3
2012-10-02 12
2012-10-03 40
2012-10-05 24
2012-10-06 18
So at date 2012-10-01 there was 3 rows in DB where Type=TDBError. At date 2012-10-02 there was 12 etc.
How should I write the SQL for this ?
Assuming SQL Server 2008 or newer:
SELECT
[Date] = CONVERT(DATE, LogDate),
[Sum] = COUNT(*)
FROM dbo.Log_Table_Name
WHERE [Type] = 'DBError'
GROUP BY CONVERT(DATE, LogDate)
ORDER BY [Date];
GROUP BY DATEPART(day, date), DATEPART(month, date), DATEPART(year, date)
You can do a group by the parts of the time
GROUP BY date(log_date), month(log_date), day(log_date)
Select Cast(FLOOR(CAST(DATE as float)) as DateTime) as Date,COUNT(*) as [SUM]
from Log_Table_Name
Group by Cast(FLOOR(CAST(DATE as float)) as DateTime)
order by Cast(FLOOR(CAST(DATE as float)) as DateTime)

T-SQL join to get both mated and non mated start and stop records

I have a poorly designed table that I did not design and cannot fix/change because a 3rd party blackberry app that writes to it. The meat is that there is a start record and a stop record for tracking events with NO connection or validation that there is a match. The blackberry app does nothing to tie these records together. I have tried to create a join on its self and create temp tables with the begin and one with the end to full outer join them. The problem is that I have duplicate entries were the entry should be marked as having no mate. Existing data has rows with no mate on both the start and end records. I have searched SO for answers and I found some close answers that have led me this far. I know its a long post, sorry for that.
There is a single table that surprisingly has a primary key. There is no pivot/intersection table. Structure is
ID (int PK)
activityType varchar
beginEnd varchar ('begin' or 'end')
businessKey varchar nullable
date DATETIME
technician varchar
The following columns are in the table as well, but are nullable, and not important to the query.
dateSubmitted DATETIME
gpsLatitude float
gpsLongitude float
note varchar
odometer int
The query that I have now that still leaves dupes: Showing and sorting ID and EndID are for debugging only
DECLARE #DateFrom DATETIME
DECLARE #DateTo DATETIME
SET #DateFrom='20101101'
SET #DateTo='20101102'
DECLARE #Incomplete VARCHAR(15)
SET #Incomplete = 'Incomplete'
DECLARE #StartEvents TABLE
(
[id] [numeric](19, 0) NOT NULL,
[activityType] [varchar](255) NOT NULL,
[beginEnd] [varchar](255) NULL,
[businessKey] [varchar](255) NULL,
[date] [datetime] NOT NULL,
[dateSubmitted] [datetime] NULL,
[gpsLatitude] [float] NULL,
[gpsLongitude] [float] NULL,
[note] [varchar](255) NULL,
[odometer] [int] NULL,
[technician] [varchar](255) NOT NULL
)
INSERT #StartEvents
([ID],[activityType],[beginEnd],[businessKey],[date],[dateSubmitted],[gpsLatitude]
,[gpsLongitude],[note],[odometer],[technician])
SELECT *
FROM dbo.TimeEntry
WHERE
[date] between #DateFrom AND #DateTo
AND beginEnd = 'Begin'
--AND [technician] = 'FRED'
ORDER by technician
------------------------------------------------------------
DECLARE #EndEvents TABLE
(
[id] [numeric](19, 0) NOT NULL,
[activityType] [varchar](255) NOT NULL,
[beginEnd] [varchar](255) NULL,
[businessKey] [varchar](255) NULL,
[date] [datetime] NOT NULL,
[dateSubmitted] [datetime] NULL,
[gpsLatitude] [float] NULL,
[gpsLongitude] [float] NULL,
[note] [varchar](255) NULL,
[odometer] [int] NULL,
[technician] [varchar](255) NOT NULL
)
INSERT #EndEvents
([ID],[activityType],[beginEnd],[businessKey],[date],[dateSubmitted],[gpsLatitude]
,[gpsLongitude],[note],[odometer],[technician])
SELECT *
FROM dbo.TimeEntry
WHERE
[date] between #DateFrom AND #DateTo AND
beginEnd = 'End'
--AND [technician] = 'FRED'
ORDER by technician
-- And then a conventional SELECT
SELECT
StartEvents.id
,EndEvents.id AS EndID
,COALESCE(
StartEvents.activityType ,EndEvents.activityType ,'Not Available'
) AS ActivityType
--,StartEvents.beginEnd as [Begin]
--,EndEvents.beginEnd AS [End]
,COALESCE (
convert(VARCHAR(12), StartEvents.[date], 103),
convert(VARCHAR(12), EndEvents.[date], 103), #Incomplete
) as [Event Date]
,COALESCE (
convert(VARCHAR(12), EndEvents.[date], 103), #Incomplete
) as [End Date]
,COALESCE(
CONVERT(VARCHAR(5) , StartEvents.dateSubmitted , 108) , #Incomplete
) AS StartTime
,COALESCE(
CONVERT(VARCHAR(5) , EndEvents.dateSubmitted , 108) , #Incomplete
) AS EndTime
,COALESCE(
StartEvents.note, EndEvents.note, ''
) as [Note]
,COALESCE(
StartEvents.technician,EndEvents.technician,'Not Available'
) AS Technician
FROM
#StartEvents As StartEvents
FULL OUTER JOIN
#EndEvents AS EndEvents ON
StartEvents.technician = EndEvents.technician AND
StartEvents.businessKey = EndEvents.businessKey AND
StartEvents.activityType = EndEvents.activityType
AND convert(VARCHAR(12), StartEvents.[date], 103) = convert(VARCHAR(12), EndEvents.[date], 103)
-- WHERE
--StartEvents.[date] between #DateFrom AND #DateTo OR
--StartEvents.[dateSubmitted] between #DateFrom AND #DateTo
ORDER BY
StartEvents.Technician,
ID,ENDID
DATA:
id,activityType,beginEnd,businessKey,date,dateSubmitted,gpsLatitude,gpsLongitude,note,odometer,technician
23569,Standby,Begin,,2010-11-01 08:00:13.000,2010-11-01 08:26:45.533,34.139,-77.895,#1140,28766,barthur#fubar.com
23570,Travel,Begin,00100228002,2010-11-01 07:00:44.000,2010-11-01 08:34:15.370,35.0634,-80.7668,,18706,creneau#fubar.com
23571,Standby,End,,2010-11-01 08:30:08.000,2010-11-01 08:35:20.463,34.0918,-77.9002,#1140,28766,barthur#fubar.com
23572,Travel,Begin,00100226488,2010-11-01 08:30:41.000,2010-11-01 08:36:56.420,34.0918,-77.9002,,28766,barthur#fubar.com
23573,Travel,End,00100226488,2010-11-01 08:45:00.000,2010-11-01 08:44:15.553,34.0918,-77.9002,,28768,barthur#fubar.com
23574,OnSite,Begin,00100226488,2010-11-01 08:45:41.000,2010-11-01 09:24:23.943,34.0918,-77.9002,,0,barthur#fubar.com
23575,OnSite,End,00100226488,2010-11-01 09:30:10.000,2010-11-01 09:33:19.953,34.0918,-77.9002,,28768,barthur#fubar.com
23576,Travel,Begin,00100228137,2010-11-01 09:30:20.000,2010-11-01 09:34:57.330,34.0918,-77.9002,,28768,barthur#fubar.com
23577,Travel,End,00100228137,2010-11-01 09:45:51.000,2010-11-01 09:42:39.230,34.0918,-77.9002,,28771,barthur#fubar.com
23578,Travel,Begin,00100228138,2010-11-01 09:00:23.000,2010-11-01 09:58:22.857,34.9827,-80.5365,,18749,creneau#fubar.com
23579,OnSite,Begin,00100228137,2010-11-01 09:45:47.000,2010-11-01 10:41:10.563,34.139,-77.895,,0,barthur#fubar.com
23580,OnSite,End,00100228137,2010-11-01 10:45:43.000,2010-11-01 11:09:14.393,34.139,-77.895,,28771,barthur#fubar.com
23581,OnSite,Begin,00100228142,2010-11-01 10:45:42.000,2010-11-01 11:29:26.447,34.139,-77.895,#1015,28771,barthur#fubar.com
23582,OnSite,End,00100228142,2010-11-01 11:15:18.000,2010-11-01 11:55:28.603,34.139,-77.895,#1015,28771,barthur#fubar.com
23583,Travel,Begin,,2010-11-01 11:15:06.000,2010-11-01 11:56:01.633,34.139,-77.895,"#1142 Fuel, #1154 Tickets",28771,barthur#fubar.com
23584,Travel,End,,2010-11-01 12:00:47.000,2010-11-01 12:07:54.867,34.139,-77.895,"#1154, #1142",28774,barthur#fubar.com
23585,Travel,End,,2010-11-01 12:00:47.000,2010-11-01 12:07:55.087,34.139,-77.895,"#1154, #1142",28774,barthur#fubar.com
23586,Break,Begin,,2010-11-01 12:00:26.000,2010-11-01 12:08:06.007,34.139,-77.895,#1153,28774,barthur#fubar.com
23587,Travel,End,,2010-11-01 12:00:47.000,2010-11-01 12:08:06.040,34.139,-77.895,"#1154, #1142",28774,barthur#fubar.com
23588,Break,Begin,,2010-11-01 12:00:26.000,2010-11-01 12:08:06.070,34.139,-77.895,#1153,28774,barthur#fubar.com
23589,Travel,End,,2010-11-01 12:00:47.000,2010-11-01 12:16:02.673,34.139,-77.895,"#1154, #1142",28774,barthur#fubar.com
23590,Travel,End,,2010-11-01 12:00:47.000,2010-11-01 12:16:14.220,34.139,-77.895,"#1154, #1142",28774,barthur#fubar.com
23591,Travel,Begin,00100228000,2010-11-01 11:45:19.000,2010-11-01 12:35:46.363,35.0634,-80.7668,,18760,creneau#fubar.com
23592,Travel,Begin,00100227980,2010-11-01 13:15:14.000,2010-11-01 13:58:51.050,34.0918,-77.9002,,28774,barthur#fubar.com
23593,Travel,Begin,00100227980,2010-11-01 13:15:14.000,2010-11-01 13:59:03.830,34.0918,-77.9002,,28774,barthur#fubar.com
23594,Travel,Begin,00100227980,2010-11-01 13:15:14.000,2010-11-01 13:59:03.893,34.1594,-77.8929,,28774,barthur#fubar.com
23595,Travel,Begin,00100227980,2010-11-01 13:15:14.000,2010-11-01 13:59:03.940,34.1594,-77.8929,,28774,barthur#fubar.com
23596,Travel,Begin,00100227980,2010-11-01 13:15:14.000,2010-11-01 13:59:15.880,34.1594,-77.8929,,28774,barthur#fubar.com
23597,Travel,Begin,00100227980,2010-11-01 13:15:14.000,2010-11-01 13:59:15.927,34.2743,-77.8668,,28774,barthur#fubar.com
23598,Travel,Begin,00100227980,2010-11-01 13:15:14.000,2010-11-01 13:59:15.987,34.2743,-77.8668,,28774,barthur#fubar.com
23599,Travel,Begin,00100228166,2010-11-01 14:00:13.000,2010-11-01 14:29:45.320,35.0634,-80.7668,,18779,creneau#fubar.com
23600,Travel,End,00100227980,2010-11-01 15:15:58.000,2010-11-01 15:15:40.403,35.3414,-78.0325,,28880,barthur#fubar.com
23601,Travel,Begin,00100228205,2010-11-01 15:30:46.000,2010-11-01 15:41:41.810,35.0661,-80.8376,,18781,creneau#fubar.com
23602,OnSite,Begin,00100227980,2010-11-01 15:15:23.000,2010-11-01 15:59:45.203,35.3873,-77.9395,,28880,barthur#fubar.com
23603,OnSite,End,00100227980,2010-11-01 16:15:22.000,2010-11-01 16:06:09.150,35.3873,-77.9395,,28880,barthur#fubar.com
23604,Travel,Begin,00100228007,2010-11-01 16:15:15.000,2010-11-01 16:15:25.253,35.3873,-77.9395,,28880,barthur#fubar.com
23605,Travel,Begin,,2010-11-01 16:15:12.000,2010-11-01 16:20:49.933,35.0445,-80.8227,Return trip home,18785,creneau#fubar.com
23606,Travel,End,00100228007,2010-11-01 16:30:48.000,2010-11-01 16:26:43.360,35.3873,-77.9395,,28884,barthur#fubar.com
23607,Travel,End,,2010-11-01 17:30:14.000,2010-11-01 17:23:57.897,35.2724,-81.1577,Return trip home,18822,creneau#fubar.com
23608,OnSite,Begin,00100228007,2010-11-01 16:30:48.000,2010-11-01 18:38:32.700,35.3941,-77.994,,28880,barthur#fubar.com
23609,Travel,Begin,00100228209,2010-11-01 17:45:16.000,2010-11-01 18:39:05.683,35.3941,-77.994,,28884,barthur#fubar.com
23610,OnSite,End,00100228007,2010-11-01 17:45:52.000,2010-11-01 18:41:36.980,35.3941,-77.994,,28884,barthur#fubar.com
23611,OnSite,Begin,00100228209,2010-11-01 18:00:38.000,2010-11-01 18:42:12.763,35.3941,-77.994,,28888,barthur#fubar.com
23612,OnSite,End,00100228209,2010-11-01 18:30:44.000,2010-11-01 18:43:29.123,35.3941,-77.994,,28888,barthur#fubar.com
23613,Standby,Begin,,2010-11-01 18:30:58.000,2010-11-01 18:45:28.857,35.3941,-77.994,#1157 ergo,28888,barthur#fubar.com
23614,Standby,End,,2010-11-01 18:45:26.000,2010-11-01 18:46:01.167,35.3941,-77.994,#1157 ergo redo,28888,barthur#fubar.com
23615,Travel,Begin,,2010-11-01 18:45:24.000,2010-11-01 18:47:37.803,35.3941,-77.994,RTN,28888,barthur#fubar.com
23616,Travel,End,,2010-11-01 20:45:05.000,2010-11-01 20:34:39.433,34.139,-77.895,#1142 Fueled,28990,barthur#fubar.com
In this image you see that the highlighted rows are showing 6 end times with the same begin times. and records 14 and 15 show 2 begins and no ends.
Here is a method which abuses the row_number function. Please check the comments for some explanation.
;with Seq as (
-- Create a master sequence of events
-- Trust the date column to be accurate (don't match a Begin to an earlier End)
select id, activitytype, beginend
, coalesce(businesskey, '') as businesskey -- Needed to match nulls as equal
, [date], technician, note
, row_number() over (partition by technician, businesskey, activitytype order by [date], beginend, id) as rownumber
from TimeEntry
)
select b.id as BeginID
, e.id as EndID
, coalesce(b.technician, e.technician) as Technician
, coalesce(b.businesskey, e.businesskey) as BusinessKey
, coalesce(b.activitytype, e.activitytype) as ActivityType
, coalesce(convert(char(10), b.[date], 103), 'Incomplete') as BeginDate
, coalesce(convert(char(10), e.[date], 103), 'Incomplete') as EndDate
, coalesce(convert(char(5), b.[date], 108), 'Incomplete') as BeginTime
, coalesce(convert(char(5), e.[date], 108), 'Incomplete') as EndTime
, b.note as BeginNote
, e.note as EndNote
from (select * from Seq where beginend = 'Begin') b -- Get all Begins
full outer join (select * from Seq where beginend = 'End') e -- Get all Ends
on b.technician = e.technician
and b.businesskey = e.businesskey
and b.activitytype = e.activitytype
and b.rownumber = e.rownumber - 1 -- Match a Begin with only the very next End of that type
order by coalesce(b.[date], e.[date])
, coalesce(b.id, e.id)
, coalesce(b.technician, e.technician)
, coalesce(b.businesskey, e.businesskey)
, coalesce(b.activitytype, e.activitytype)
And the SQL Fiddle should anyone want the DDL or demo.
Using CTE's and Row_Number() I think I can return what you are looking for. The key is to only perform your full outer by joining on adajacent rows based upon an ordering of technician, businesskey, activitytype. Anyway here it is:
WITH EventsCTE
AS
(
select id, technician,businessKey,activityType,
CAST(date as date) As date, dateSubmitted,
beginEnd, note,
ROW_NUMBER() OVER
(Partition BY technician,
businesskey, activityType
ORDER BY dateSubmitted) as rowN
from TimeEntry
),
BeginEventsCTE
AS
(
select id, technician,businessKey,activityType,
CAST(date as date) As date,
dateSubmitted, note, rowN
from EventsCTE
WHERE beginEnd = 'Begin'
),
EndEventsCTE
AS
(
select id, technician,businessKey,
activityType, CAST(date as date) As date,
dateSubmitted, note, rowN
from EventsCTE
WHERE beginEnd = 'End'
)
SELECT B.id,
E.id As EndId,
COALESCE(B.activityType ,E.activityType ,'Not Available') AS ActivityType,
COALESCE(convert(VARCHAR(12), B.[date], 103), 'Incomplete') As EventDate,
COALESCE(convert(VARCHAR(12), E.[date], 103), 'Incomplete') As EndDate,
COALESCE(CONVERT(VARCHAR(5) , B.dateSubmitted , 108) , 'Incomplete') As StartTime,
COALESCE(CONVERT(VARCHAR(5) , E.dateSubmitted , 108) , 'Incomplete') As EndTime,
COALESCE(B.Note, E.note, '') as Note,
COALESCE(B.technician, E.technician, 'Not Available') As Technician --, B.rowN, E.rowN
FROM BeginEventsCTE B
FULL OUTER join EndEventsCTE E
ON B.technician = E.technician AND
B.businessKey = E.businessKey AND
B.activityType = E.activityType AND
B.date = E.date AND
B.rowN = E.rowN - 1
ORDER BY
COALESCE(B.Technician, E.Technician),
COALESCE(b.id,E.id)
And here's the results:

Correlated join with outer fields as parameters

I need a way to join a table with a function's results. I'm not sure if it's possible and somehow I don't think it's a good idea. Let me try and explain the situation.
There is a table [Entities]:
[ID] [Description] [kWh] [kVArh] [kVAh] [OfferID] [CustomOfferID]
Another table [Data]:
[ID] [Timestamp] [Value]
And a function:
[Calc] (#offer, #customOffer, #kWh, #kVArh, #kVAh, #dtStart, #dtEnd)
So you can see there are entities, the entities' data (usage) and a function to calculate the cost.
I need to display the usage and cost of multiple entites:
[Description] [MWh] [MVA] [Cost]
[MWh] will be a SUM of all the entity's data over the period; [MVA] will be the MAX of all the data over the period; and [Cost] will SUM the [Cost] field from the sub query (function).
The query I figured would do the jobs looks like this:
SELECT [tc].[ID], [tc].[Desc]
, SUM([kWh].[Value]) / 1000 AS [MWh]
, MAX([kVAh].[Value]) / 1000 AS [MVA]
, SUM([cost].[Cost])
FROM [Tree_Cost] AS [tc]
INNER JOIN [Data] AS [kWh] ON [tc].[kWh] = [kWh].[ID]
INNER JOIN [Data] AS [kVAh] ON [tc].[kVAh] = [kVAh].[ID]
INNER JOIN
(
SELECT [tc].[ID], [Cost]
FROM [Calc] ([tc].[Offer_ID], [tc].[OfferCustom_ID], [tc].[kWh], [tc].[KVArh], [tc].[kVAh], #dtStart, #dtEnd)
) AS [cost] ON [tc].[ID] = [cost].[ID]
WHERE [tc].[Type] = 1 AND [tc].[TypeDesc] = 'GF_K_M'
AND [kWh].[Timestamp] BETWEEN #dtStart AND #dtEnd
AND [kVAh].[Timestamp] BETWEEN #dtStart AND #dtEnd
GROUP BY [tc].[ID], [tc].[Desc]
The real problem here is that I need to include the [ID] from the outer query in the result set of the inner query (function) in order to be able to join the two. Then I also need to be able to use the fields from the outer query as arguments for the inner query (function).
This is obviously not the way seeing as the [tc] identifier is not recognized in the inner query. So how am I supposed to accomplish something like this?
CREATE FUNCTION [dbo].[Calc]
( \#intOffer [int]
, \#intCustom [int]
, \#intP [int]
, \#intQ [int]
, \#intS [int]
, \#dtStart [datetime]
, \#dtEnd [datetime]
)
RETURNS TABLE
( [Entry] [nvarchar](200) NULL
, [Rate] [float] NULL
, [Unit] [nvarchar](50) NULL
, [Reading] [float] NULL
, [Cost] [float] NULL
, [DDate] [nvarchar](50) NULL
)
WITH EXECUTE AS CALLER
AS EXTERNAL NAME [OfferCalcLite].[UserDefinedFunctions].[SqlArray]
I'm not sure I understand correctly. Perhaps you can completely drop the JOIN:
INNER JOIN
(
SELECT [tc].[ID], [Cost]
FROM [Calc] ([tc].[Offer_ID], [tc].[OfferCustom_ID], [tc].[kWh], [tc].[KVArh], [tc].[kVAh], #dtStart, #dtEnd)
) AS [cost] ON [tc].[ID] = [cost].[ID]
and change:
, SUM([cost].[Cost])
into:
, SUM( [Calc] ( [tc].[Offer_ID]
, [tc].[OfferCustom_ID]
, [tc].[kWh]
, [tc].[KVArh]
, [tc].[kVAh]
, #dtStart, #dtEnd
)
) AS Cost