I am trying to compare a plan against a history to see if there are steps omitted or if unplanned steps have been included. The regular, planned history items as well as the anomalies are intended for an elaborate report. The problem is, I have a column in each joined table that I need for sorting, but due to the full outer join
I cannot guarantee them to be not null
, which breaks sorting. I'll continue with a simplified example, where the plan corresponds to a recipe:
Table: RecipeSteps
Id | RecipeId | Position | Message
----+----------+----------+----------------------------
50 | 123 | 1 | Wash vegetables
51 | 123 | 2 | Peel vegetables
52 | 123 | 3 | Cut meat
53 | 123 | 4 | Turn on stove
54 | 123 | 5 | Cook and stir it
55 | 123 | 6 | Turn off stove
----+----------+----------+----------------------------
... and the history to the actual steps while cooking:
Table: History
Id | TimeStamp | Session | StepId | Message
----+-----------+---------+--------+-----------------------------
90 | 00:01:00 | 321 | NULL | Chef entered kitchen
91 | 00:02:00 | 321 | 51 | Chef peeled vegetables
92 | 00:03:00 | 321 | 52 | Chef cut meat
93 | 00:04:00 | 321 | NULL | Chef picked his nose
94 | 00:05:00 | 321 | 53 | Chef turned on stove
95 | 00:06:00 | 321 | 54 | Chef started cooking
96 | 00:10:00 | 321 | NULL | Chef left kitchen
97 | 01:00:00 | 321 | NULL | FIRE!
----+-----------+---------+--------+-----------------------------
Now, I want to compare these tables in a result table with a full outer join
similar to this:
Table: Result
Sorting | TimeStamp | Position | Message | Comment
--------+-----------+----------+------------------------+---------------
1 | 00:01:00 | NULL | Chef entered kitchen | unplanned
2 | NULL | 1 | Wash vegetables | omitted
3 | 00:02:00 | 2 | Chef peeled vegetables | planned
4 | 00:03:00 | 3 | Chef cut meat | planned
5 | 00:04:00 | NULL | Chef picked his nose | unplanned
6 | 00:05:00 | 4 | Chef turned on stove | planned
7 | 00:06:00 | 5 | Chef started cooking | planned
8 | NULL | 6 | Turn off stove | omitted
9 | 00:10:00 | NULL | Chef left kitchen | unplanned
10 | 01:00:00 | NULL | FIRE! | unplanned
--------+-----------+----------+------------------------+---------------
Then the reader of the report sees:
- Entering/leaving the kitchen is not part of the recipe, but it has been recorded anyway.
- The chef forgot to wash the vegetables (Ugh!) or the step has not been recorded for some reason (Phew!).
- History continues as planned, with the exception of the chef picking his nose in between (double-Ugh!)
- The chef forgot to turn off the stove, which caused the fire, in the end.
I came up with the following SELECT
statement, selecting from this query with ORDER BY Sorting ASC
, but I still have a problem to fill the Sorting
criterion:
SELECT
0 Sorting, -- this has to be replaced
h.TimeStamp,
r.Position,
'...' Message, -- case-when-construct, not important for the question
'...' Comment -- case-when-construct, not important for the question
FROM RecipeSteps r
FULL OUTER JOIN History h
ON h.StepId = r.Id
WHERE (r.RecipeId = 123 OR r.RecipeId IS NULL)
AND (h.Session = 321 OR h.Session IS NULL)
There is a natural order in the history given by the timestamps and one in the recipe given by the Position
column. As they are never both null
simultaneously, I could create the Sorting
column based on these, but I couldn't figure out, so far, how to achieve this.
I know I can sort both separately, enumerate and compare both sequences etc. programmatically, but I'm curious if this is also possible with non-procedural SQL.
CASE
statement to set theSorting
values and/or windowing functions. Unfortunately, it's not clear how that would happen with the data as shown. How do you know where to place a skippedRecipeStep
, relative to "unplanned"History
rows? What if theHistory
rows and theRecipeSteps
rows don't follow the same order? How should that impact the placement of skipped steps? For instance, id the chef cut the meat, then peeled the veggies, should the skipped "wash the veggies" precede or follow cutting the meat?omitted
as well asunplanned
rows for each of the swapped rows, if needed. The regular entries (both not null) are kind of "anchor-points", where the sequences could be "synchronized" and the rest is ordered by timestamp or position, repectively. I'll rethink theCASE
statements I had and have a look at windowing functions, tomorrow. Thanks.