I agree with Aaron that RAISERROR...WITH NOWAIT
can be very useful and is probably the way to go if you have full control over the script that is being generated.
However, if a long script is currently executing and you don't have the ability to change the script in order to add RAISERROR
calls, there are also less direct ways to get this information.
Test script
Here is a test script you can run to help demonstrate the two approaches below:
SELECT 1
WAITFOR DELAY '00:00:15'
SELECT 2
WAITFOR DELAY '00:00:15'
SELECT 3
sp_whosiasctive
While running this script, you can use sp_whoisactive to view the current server activity. You can often view the query plan for the specific statement that is currently executing. In my case, I see the following because the WAITFOR
statement is most likely to be running at any given moment in time:
Using sys.dm_exec_requests.statement_start_offset
Alternatively, Conor Cunningham also has a post on extracting the statement from sys.dm_exec_query_stats
AND sys.dm_exec_sql_text
. I don't believe this has been incorporated into sp_whoisactive
yet, but you can use a query like the following to see both the current executing statement and the overall batch.
SELECT er.session_Id AS spid
--Use the full batch text and the start/end offset of the currect statement to figure
--out the SQL that is currently executing. This logic is based on the blog post above
--but has been updated in light of strange cases in SQL Server that caused the original
--blog post logic to crash with out of bounds errors on the SUBSTRING operation.
, SUBSTRING (qt.text
, (CASE WHEN er.statement_start_offset > DATALENGTH(qt.text)
THEN 0 ELSE er.statement_start_offset/2 END)+1
, (CASE WHEN er.statement_end_offset <= 0 THEN DATALENGTH(qt.text)
ELSE er.statement_end_offset
END - CASE WHEN er.statement_start_offset > DATALENGTH(qt.text)
THEN 0 ELSE er.statement_start_offset/2 END)
+ 1
) AS query
, qt.text AS parent_query
FROM sys.dm_exec_requests er
JOIN sys.dm_exec_sessions s
ON s.session_id = er.session_id
AND s.session_id <> @@SPID -- Ignore this current statement.
AND s.is_user_process = 1 -- Ignore system spids.
AND s.program_name NOT LIKE '%SQL Server Profiler%' -- Ignore profiler traces
OUTER APPLY sys.dm_exec_sql_text(er.sql_handle)as qt
ORDER BY spid
This works although I am not sure how to handle the date (sample is not a date):
Select DATENAME(month, [Date]) as 'date'
, SUM(Case When Operator = 'Operator 1' then Invoice1 end) as 'Sum of Invoice 1 for Operator 1'
, SUM(Case When Operator = 'Operator 1' then Invoice2 end) as 'Sum of Invoice 2 for Operator 1'
, SUM(Case When Operator = 'Operator 2' then Invoice1 end) as 'Sum of Invoice 1 for Operator 2'
, SUM(Case When Operator = 'Operator 2' then Invoice2 end) as 'Sum of Invoice 2 for Operator 2'
, COUNT(distinct Client) as 'Count of Distinct Clients'
, COUNT(distinct Entity) as 'Count of Distinct Entities'
From @data
Group By [date];
Output:
date | Sum of Invoice 1 for Operator 1 | Sum of Invoice 2 for Operator 1 | Sum of Invoice 1 for Operator 2 | Sum of Invoice 2 for Operator 2 | Count of Distinct Clients | Count of Distinct Entities
January | 40 | 50 | 130 | 140 | 2 | 4
February | 60 | 70 | 160 | 170 | 2 | 4
Your data (replaced by real dates):
Declare @data TABLE ([Date] datetime, Invoice1 int, Invoice2 int, Operator varchar(10), Client varchar(8), Entity varchar(10));
INSERT INTO @data(Date, Invoice1, Invoice2, Operator, Client, Entity)
VALUES
('20150101', 10, 15, 'Operator 1', 'Client 1', 'Entity A'),
('20150201', 20, 25, 'Operator 1', 'Client 1', 'Entity B'),
('20150101', 30, 35, 'Operator 1', 'Client 2', 'Entity C'),
('20150201', 40, 45, 'Operator 1', 'Client 2', 'Entity D'),
('20150101', 50, 55, 'Operator 2', 'Client 1', 'Entity E'),
('20150201', 70, 75, 'Operator 2', 'Client 2', 'Entity F'),
('20150101', 80, 85, 'Operator 2', 'Client 1', 'Entity G'),
('20150201', 90, 95, 'Operator 2', 'Client 2', 'Entity H')
;
Best Answer
There are tens of different ways to do this in SQL. Lets start with the simple correlated subquery (mind the fancy name, once you see and write a few of them, they are very easy to understand):
Another simple way would be to first find the biggest score for each stage using
GROUP BY
(in a subquery, either a derived table or a CTE) and thenJOIN
back to the original table:A more modern way would be to use window functions (available in your SQL Server versions), i.e. the
RANK()
function, so first you get the "rank" of everyone per stage and then select only the ones withrank=1
. This can also be done with either a derived table or a CTE: