December 22, 2016 at 6:05 am
Hi,
I have the table below:
CREATE TABLE [dbo].[ATABLE](
[EVENT_ID] [int] NOT NULL,
[EVENT_RANK] [int] NULL,
[MIN_AGE] [float] NULL,
[MAX_AGE] [float] NULL
) ON [PRIMARY]
GO
INSERT [dbo].[ATABLE] ([EVENT_ID], [EVENT_RANK], [MIN_AGE], [MAX_AGE]) VALUES (1205, 1, NULL, 55.00000000089)
GO
INSERT [dbo].[ATABLE] ([EVENT_ID], [EVENT_RANK], [MIN_AGE], [MAX_AGE]) VALUES (1205, 2, 23, NULL)
GO
INSERT [dbo].[ATABLE] ([EVENT_ID], [EVENT_RANK], [MIN_AGE], [MAX_AGE]) VALUES (1205, 3, 15, 22)
GO
INSERT [dbo].[ATABLE] ([EVENT_ID], [EVENT_RANK], [MIN_AGE], [MAX_AGE]) VALUES (1205, 4, 23, 29)
GO
What I would like to do is to return a running maximum and a running minimum
but with the added complexity that (sorting by rank ID) if the lower rank min age is higher than the current min age and the lower rank max age is lower than the current max age
that the running minimum and maximum ignores these values
in this case the running min and max at the end would be
Minimum 23
Maximum 29
I am struggling to get a windowing function to help with this and would very much welcome your help.
See below my mess:
SELECT
EVENT_ID, EVENT_RANK,
MIN_AGE,
MAX_AGE,
MAX(MIN_AGE) OVER (PARTITION BY EVENT_ID ORDER BY EVENT_RANK) AS C_MIN1,
MIN(MAX_AGE) OVER (PARTITION BY EVENT_ID ORDER BY EVENT_RANK) AS C_MAX1,
CASE
WHEN MAX_AGE < MAX(MIN_AGE) OVER (PARTITION BY EVENT_ID ORDER BY EVENT_RANK)
OR MAX_AGE > MIN(MAX_AGE) OVER (PARTITION BY EVENT_ID ORDER BY EVENT_RANK)
THEN NULL
ELSE MAX(MIN_AGE) OVER (PARTITION BY EVENT_ID ORDER BY EVENT_RANK) END AS C_MIN,
CASE
WHEN MIN_AGE > MIN(MAX_AGE) OVER (PARTITION BY EVENT_ID ORDER BY EVENT_RANK)
OR MIN_AGE < MAX(MIN_AGE) OVER (PARTITION BY EVENT_ID ORDER BY EVENT_RANK)
THEN NULL
ELSE MIN(MAX_AGE) OVER (PARTITION BY EVENT_ID ORDER BY EVENT_RANK) END AS C_MAX,
CASE
WHEN MAX_AGE < MAX(MIN_AGE) OVER (PARTITION BY EVENT_ID ORDER BY EVENT_RANK) THEN 0
WHEN MIN_AGE > MIN(MAX_AGE) OVER (PARTITION BY EVENT_ID ORDER BY EVENT_RANK) THEN 0
ELSE 1 END AS VALID
FROM ATABLE
any help would be very gratefully appreciated.
Thanks
Oliver
December 22, 2016 at 6:31 am
Can you amend the sample data please?
😎
Running it returns this error
Msg 207, Level 16, State 1, Line 32
Invalid column name 'CALIBRATION_TYPE_ID'.
December 22, 2016 at 6:43 am
amended, sorry about that.
I am going to try a while loop as another option.
Thanks,
Oliver
December 22, 2016 at 7:24 am
olibbhq (12/22/2016)
Hi,What I would like to do is to return a running maximum and a running minimum
but with the added complexity that (sorting by rank ID) if the lower rank min age is higher than the current min age and the lower rank max age is lower than the current max age
that the running minimum and maximum ignores these values
in this case the running min and max at the end would be
Minimum 23
Maximum 29
- There is no Rank ID, but you do have Event_ID & Event_Rank. I assume Event_Rank is your sort column.
- Will there be multiple Event_IDs that need to be partitioned? I assume so, but it would be nice if your sample data showed that as well.
- Rank has different meaning depending on context. 1st place is higher than 2nd place, but 1 is lower than 2. Can you clarify what you mean by lower rank? I assume you mean numerically at this point.
- You didn't describe the valid column. What is that for?
Can you add more sample data and provide the expected result? It would also be helpful to provide the business (plain English) description of the results. Something like "minimum age is the oldest young person at the most prestigious event"... That can help us to hone in on the solution.
Based on my assumptions, these both get your expected results:
--INNER JOIN
SELECT A1.EVENT_ID,
Min_Age = min(CASE WHEN A1.MIN_AGE > A2.MIN_AGE THEN A1.MIN_AGE ELSE NULL END), --the minimum of the larger minimum age
Max_Age = max(CASE WHEN A1.MAX_AGE < A2.MAX_AGE THEN A1.MAX_AGE ELSE NULL END ) --the maximum of the smaller maximum age
FROM dbo.ATABLE AS A1 --join the table to itself to compare event ranks
INNER JOIN dbo.ATABLE AS A2
ON A1.EVENT_RANK > A2.EVENT_RANK --compare each rank to all of the ranks beneath it
AND A1.EVENT_ID = A2.EVENT_ID
GROUP BY A1.EVENT_ID;
--CROSS APPLY
SELECT o.EVENT_ID,
Min_Age = MIN(oi.Max_Min),
Max_Age = MAX(oi.Min_Max)
FROM dbo.ATABLE AS o
CROSS APPLY
( SELECT Min_Max = MIN(i.MAX_AGE),
Max_Min = MAX(i.MIN_AGE)
FROM dbo.ATABLE AS i
WHERE i.EVENT_ID = o.EVENT_ID
AND i.EVENT_RANK > o.EVENT_RANK
) AS oi
GROUP BY o.EVENT_ID;
I'm not fond of either solution. I have concerns about how well these will scale with a large number of rows, especially the inner join version. I'm hoping someone else here has something better.
Wes
(A solid design is always preferable to a creative workaround)
December 22, 2016 at 7:30 am
Can we get some expected results? This gives me a right result at the end, but i'm pretty sure EVENT_RANK 3 is wrong with a C_MIN of 23, and a C_MAX of 22.#
SELECT *,
CASE WHEN A.MIN_AGE > ISNULL(LAG(A.MIN_AGE) OVER (PARTITION BY EVENT_ID ORDER BY EVENT_RANK), A.MIN_AGE)
THEN A.MIN_AGE
ELSE ISNULL(LAG(A.MIN_AGE) OVER (PARTITION BY EVENT_ID ORDER BY EVENT_RANK), A.MIN_AGE) END AS C_MIN,
CASE WHEN A.MAX_AGE > ISNULL(LAG(A.MAX_AGE) OVER (PARTITION BY EVENT_ID ORDER BY EVENT_RANK), A.MAX_AGE)
THEN A.MAX_AGE
ELSE ISNULL(LAG(A.MAX_AGE) OVER (PARTITION BY EVENT_ID ORDER BY EVENT_RANK), A.MAX_AGE) END AS C_MAX
FROM ATABLE A
ORDER BY A.EVENT_ID;
Thom~
Excuse my typos and sometimes awful grammar. My fingers work faster than my brain does.
Larnu.uk
December 22, 2016 at 7:37 am
Thank you for the replies.
The required result is:
I have written a while loop which seems to work but my word it is clunky.
DECLARE
@RANK_ID INT,
@MAX_RANK_ID INT,
@RUNNING_MIN_AGE FLOAT = NULL,
@RUNNING_MAX_AGE FLOAT = NULL
DECLARE @VALID_TABLE TABLE (EVENT_ID INT, EVENT_RANK INT, MIN_AGE FLOAT, MAX_AGE FLOAT, RUNNING_MIN FLOAT, RUNNING_MAX FLOAT, VALID INT)
SET @RANK_ID = 1
SELECT @MAX_RANK_ID = MAX(EVENT_RANK) FROM ATABLE
WHILE @RANK_ID < @MAX_RANK_ID + 1
BEGIN
DECLARE
@MIN_AGE FLOAT,
@MAX_AGE FLOAT,
@VALID INT = 0
SELECT @MIN_AGE = MIN_AGE, @MAX_AGE = MAX_AGE FROM ATABLE WHERE EVENT_RANK = @RANK_ID
SELECT @RUNNING_MIN_AGE = RUNNING_MIN, @RUNNING_MAX_AGE = RUNNING_MAX FROM @VALID_TABLE WHERE EVENT_RANK = @RANK_ID - 1
IF @RUNNING_MAX_AGE IS NULL BEGIN
SET @RUNNING_MAX_AGE = @MAX_AGE
SET @VALID = 1 END
IF @RUNNING_MIN_AGE IS NULL BEGIN
SET @RUNNING_MIN_AGE = @MIN_AGE
SET @VALID = 1 END
IF @MAX_AGE < @RUNNING_MAX_AGE AND @MAX_AGE > @RUNNING_MIN_AGE BEGIN
SET @RUNNING_MAX_AGE = @MAX_AGE
SET @VALID = 1 END
IF @MIN_AGE > @RUNNING_MIN_AGE AND @MIN_AGE < @RUNNING_MAX_AGE BEGIN
SET @RUNNING_MIN_AGE = @MIN_AGE
SET @VALID = 1 END
INSERT INTO @VALID_TABLE (EVENT_ID, EVENT_RANK, MIN_AGE, MAX_AGE, RUNNING_MIN, RUNNING_MAX, VALID)
SELECT EVENT_ID, EVENT_RANK, MIN_AGE, MAX_AGE, @RUNNING_MIN_AGE, @RUNNING_MAX_AGE, @VALID
FROM ATABLE
WHERE EVENT_RANK = @RANK_ID
SET @RANK_ID = @RANK_ID + 1
END
SELECT * FROM @VALID_TABLE
December 22, 2016 at 7:41 am
I can't see the image.
Please also provide more sample data.
Wes
(A solid design is always preferable to a creative workaround)
December 22, 2016 at 7:46 am
For those that can't see the image above.
SELECT 1205, 1 AS EVETN_RANK, NULL as MIN_AGE, 55 AS MAX_AGE, NULL AS RUNNING_MIN, 55 AS RUNNING_MAX, 1 AS VALID
UNION
SELECT 1205, 2, 23, NULL, 23, 55, 1
UNION
SELECT 1205, 3, 15, 22, 23, 55, 0
UNION
SELECT 1205, 4, 23, 29, 23, 29, 1;
EVENT_ID EVENT_RANK MIN_AGE MAX_AGE RUNNING_MIN RUNNING_MAX VALID
----------- ----------- ----------- ----------- ----------- ----------- -----------
1205 1 NULL 55 NULL 55 1
1205 2 23 NULL 23 55 1
1205 3 15 22 23 55 0
1205 4 23 29 23 29 1
To the OP, this is how you should supply Expected results.
Thom~
Excuse my typos and sometimes awful grammar. My fingers work faster than my brain does.
Larnu.uk
December 22, 2016 at 7:50 am
Thank you for posting the table Thom and for your window function. I understand what you have done and I too get the correct results in a much more elegant method than I obtain.
The last piece of logic is that
IF MAX_AGE < RUNNING_MAX_AGE AND MAX_AGE > RUNNING_MIN_AGE
Then
RUNNING_MAX_AGE = MAX_AGE
IF MIN_AGE > RUNNING_MIN_AGE AND MIN_AGE < RUNNING_MAX_AGE
Then
RUNNING_MIN_AGE = MIN_AGE
I don't think the window function covers this example?
Many Thanks,
Oliver
December 22, 2016 at 7:54 am
olibbhq (12/22/2016)
Thank you for posting the table Thom and for your window function. I understand what you have done and I too get the correct results in a much more elegant method than I obtain.The last piece of logic is that
IF MAX_AGE < RUNNING_MAX_AGE AND MAX_AGE > RUNNING_MIN_AGE
Then
RUNNING_MAX_AGE = MAX_AGE
IF MIN_AGE > RUNNING_MIN_AGE AND MIN_AGE < RUNNING_MAX_AGE
Then
RUNNING_MIN_AGE = MIN_AGE
I don't think the window function covers this example?
Many Thanks,
Oliver
it doesn't, working on another solution now.
Thom~
Excuse my typos and sometimes awful grammar. My fingers work faster than my brain does.
Larnu.uk
December 22, 2016 at 8:05 am
This gives me the right results, however, this is quite a small dataset, you might want to check on others.
Also, what is the logic for VALID? I have omitted this for the moment.
SELECT A.EVENT_ID,
A.EVENT_RANK,
A.MIN_AGE,
A.MAX_AGE,
CASE WHEN A.MIN_AGE < MAX(R.MIN_AGE)
THEN MAX(R.MIN_AGE)
ELSE A.MIN_AGE END AS C_MIN,
CASE WHEN ISNULL(A.MAX_AGE, MAX(R.MAX_AGE)) <= MAX(R.MAX_AGE) AND ISNULL(A.MAX_AGE, MAX(R.MAX_AGE)) <= ISNULL(LAG(A.MAX_AGE) OVER (PARTITION BY A.EVENT_ID ORDER BY A.EVENT_RANK), MAX(R.MAX_AGE))
THEN MAX(R.MAX_AGE)
ELSE A.MAX_AGE END AS C_MAX
FROM ATABLE A
JOIN ATABLE r ON A.EVENT_RANK >= r.EVENT_RANK
GROUP BY A.EVENT_ID,
A.EVENT_RANK,
A.MIN_AGE,
A.MAX_AGE
ORDER BY A.EVENT_ID,
A.EVENT_RANK;
Others, feel free to complain about query plans on that SQL, I'm sure it is horrid <3 😎
Thom~
Excuse my typos and sometimes awful grammar. My fingers work faster than my brain does.
Larnu.uk
December 22, 2016 at 8:15 am
Thank you Thom, I am very grateful.
I will try this with a larger dataset and see how it gets on.
I am curious you moved away from windowing to joining is this because windowing cant handle the task?
Many Thanks and to the others that have helped me.
Oliver
December 22, 2016 at 8:21 am
olibbhq (12/22/2016)
Thank you Thom, I am very grateful.I will try this with a larger dataset and see how it gets on.
I am curious you moved away from windowing to joining is this because windowing cant handle the task?
Many Thanks and to the others that have helped me.
Oliver
I have actually used both in the MAX (ugly, but works).
Partly, yes. The main reason being that you want a running max, but also wanted to check the prior row.
To be honest, I probably should have still done this with a window function, and used ROWS PRECENDING and CURRENT ROW (I always foget they exist). I'm going to experiment and see if it can be fully windowed (unless someone beats me to it).
Thom~
Excuse my typos and sometimes awful grammar. My fingers work faster than my brain does.
Larnu.uk
December 22, 2016 at 8:32 am
Fully Windowed, and VALID included.
WITH CTE (EVENT_ID, EVENT_RANK, MIN_AGE, MAX_AGE, C_MIN, C_MAX) AS
(
SELECT A.EVENT_ID,
A.EVENT_RANK,
A.MIN_AGE,
A.MAX_AGE,
CASE WHEN ISNULL(A.MIN_AGE,0) < MAX(A.MIN_AGE) OVER (PARTITION BY A.EVENT_ID ORDER BY A.EVENT_RANK
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW)
THEN MAX(A.MIN_AGE) OVER (PARTITION BY A.EVENT_ID ORDER BY A.EVENT_RANK
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW)
ELSE A.MIN_AGE END AS C_MIN,
CASE WHEN ISNULL(A.MAX_AGE,0) < MAX(A.MAX_AGE) OVER (PARTITION BY A.EVENT_ID ORDER BY A.EVENT_RANK
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW)
AND ISNULL(A.MAX_AGE,0) <= ISNULL(LAG(A.MAX_AGE) OVER (PARTITION BY A.EVENT_ID ORDER BY A.EVENT_RANK), A.MAX_AGE)
THEN MAX(A.MAX_AGE) OVER (PARTITION BY A.EVENT_ID ORDER BY A.EVENT_RANK
ROWS BETWEEN UNBOUNDED PRECEDING
AND CURRENT ROW)
ELSE A.MAX_AGE END AS C_MAX
FROM ATABLE A)
SELECT *,
CASE WHEN C.MAX_AGE < C.C_MIN THEN 0
WHEN C.MIN_AGE > C.C_MAX THEN 0
ELSE 1 END AS VALID
FROM CTE C
ORDER BY C.EVENT_ID,
C.EVENT_RANK;
If this really does work, the "MARK AS SOLUTION" button is always appreciated 😉
Thom~
Excuse my typos and sometimes awful grammar. My fingers work faster than my brain does.
Larnu.uk
December 22, 2016 at 8:48 am
Thanks Thom, I will run this through and check it out.
Will also need to check out the proceeding functions.
Many Thanks,
Oliver
Viewing 15 posts - 1 through 15 (of 17 total)
You must be logged in to reply to this topic. Login to reply