Labels

admin (1) aix (1) alert (1) always-on (2) Architecture (1) aws (3) Azure (1) backup (3) BI-DWH (10) Binary (3) Boolean (1) C# (1) cache (1) casting (3) cdc (1) certificate (1) checks (1) cloud (3) cluster (1) cmd (7) collation (1) columns (1) compilation (1) configurations (7) Connection-String (2) connections (6) constraint (6) copypaste (2) cpu (2) csv (3) CTE (1) data-types (1) datetime (23) db (547) DB2 (1) deadlock (2) Denali (7) device (6) dotNet (5) dynamicSQL (11) email (5) encoding (1) encryption (4) errors (124) excel (1) ExecutionPlan (10) extended events (1) files (7) FIPS (1) foreign key (1) fragmentation (1) functions (1) GCP (2) gMSA (2) google (2) HADR (1) hashing (3) in-memory (1) index (3) indexedViews (2) insert (3) install (10) IO (1) isql (6) javascript (1) jobs (11) join (2) LDAP (2) LinkedServers (8) Linux (15) log (6) login (1) maintenance (3) mariadb (1) memory (4) merge (3) monitoring (4) MSA (2) mssql (444) mssql2005 (5) mssql2008R2 (20) mssql2012 (2) mysql (36) MySQL Shell (5) network (1) NoSQL (1) null (2) numeric (9) object-oriented (1) offline (1) openssl (1) Operating System (4) oracle (7) ORDBMS (1) ordering (2) Outer Apply (1) Outlook (1) page (1) parameters (2) partition (1) password (1) Performance (103) permissions (10) pivot (3) PLE (1) port (4) PostgreSQL (14) profiler (1) RDS (3) read (1) Replication (12) restore (4) root (1) RPO (1) RTO (1) SAP ASE (48) SAP RS (20) SCC (4) scema (1) script (8) security (10) segment (1) server (1) service broker (2) services (4) settings (75) SQL (74) SSAS (1) SSIS (19) SSL (8) SSMS (4) SSRS (6) storage (1) String (35) sybase (57) telnet (2) tempdb (1) Theory (2) tips (120) tools (3) training (1) transaction (6) trigger (2) Tuple (2) TVP (1) unix (8) users (3) vb.net (4) versioning (1) windows (14) xml (10) XSD (1) zip (1)

Outer 2 statuses

The case:
2 tables, with a FK from one to a statuses log table.
We need to select for each row in the first table the last status with it's datetime and the description of it, and also - in the same row - the datetime of the last specific stauts (in the example - 303).

there is no one absolut answer, it depend on the data in the tables and exists indexes, constraints, etc om the table.



-- create the Demo tables and data:
CREATE TABLE TableA (ID int identity(1,1) NOT NULL, SomeFkID int)
CREATE TABLE TableFk (SomeFkID int, SomeFkStatus int, SomeFkDatetime datetime, SomeFkDesc nvarchar(50))

INSERT INTO TableA (SomeFkID) 
VALUES (1), (2), (3), (4)

INSERT INTO TableFk (SomeFkID, SomeFkStatus, SomeFkDatetime, SomeFkDesc)
VALUES 
(1, 1, '2017-01-01', N'FK 1 1 1'),
(2, 1, '2017-01-02', N'FK 2 1 1'), (2, 303, '2017-01-03', N'FK 2 303 2'), (2, 3, '2017-01-04', N'FK 2 3 3'), 
(2, 303, '2017-01-05', N'FK 2 303 4'), (2, 4, '2017-01-06', N'FK 2 4 5'),
(3, 1, '2017-01-01', N'FK 3 1 1'), (3, 2, '2017-01-03', N'FK 3 21'),
(4, 1, '2017-01-05', N'FK 4 1 1'), (4, 303, '2017-01-08', N'FK 4 303 1')

SELECT * FROM TableA
SELECT * FROM TableFk

--SELECT SomeFkID, SomeFkStatus, max(SomeFkDatetime) as maxSomeFkDatetime
--FROM TableFk f
--GROUP BY SomeFkID, SomeFkStatus
--ORDER BY SomeFkID, SomeFkStatus

--SELECT ROW_NUMBER()  OVER (PARTITION BY SomeFkID ORDER BY SomeFkDatetime DESC) as rn,
-- ROW_NUMBER()  OVER (PARTITION BY SomeFkID ORDER BY (case when SomeFkStatus = 303 then SomeFkDatetime else NULL end) DESC) as rn303,
-- SomeFkID, SomeFkStatus, SomeFkDatetime, SomeFkDesc
--FROM TableFk f
--ORDER BY SomeFkID, rn

-- option 1: CTE with 2 ROW_NUMBERs
; with rnFK as
(
SELECT ROW_NUMBER()  OVER (PARTITION BY SomeFkID ORDER BY SomeFkDatetime DESC) as rn,
ROW_NUMBER()  OVER (PARTITION BY SomeFkID ORDER BY (case when SomeFkStatus = 303 then SomeFkDatetime else NULL end) DESC) as rn303,
SomeFkID, SomeFkStatus, SomeFkDatetime, SomeFkDesc
FROM TableFk f
SELECT a.ID, a.SomeFkID, 
f303.SomeFkDatetime AS YatzranStatusDate, 
f.SomeFkDesc AS ErrorDesc,
f.SomeFkStatus as FileStatusID,
f.SomeFkDatetime
FROM TableA a
JOIN rnFK f ON a.SomeFkID = f.SomeFkID
AND f.rn = 1
LEFT JOIN rnFK f303 ON a.SomeFkID = f303.SomeFkID
AND f303.rn303 = 1
AND f303.SomeFkStatus = 303


-- option 2: two OUTER APPLYs - one for the last status and one for 303 status
SELECT a.ID, a.SomeFkID, 
f303.SomeFkDatetime AS YatzranStatusDate, 
f.SomeFkDesc AS ErrorDesc,
f.SomeFkStatus as FileStatusID,
f.SomeFkDatetime
FROM TableA a
OUTER APPLY
( select top 1 SomeFkID, SomeFkStatus, SomeFkDatetime, SomeFkDesc
from TableFk where SomeFkID = a.SomeFkID
order by SomeFkDatetime desc
) f
OUTER APPLY
( select top 1 SomeFkID, SomeFkStatus, SomeFkDatetime, SomeFkDesc
from TableFk where SomeFkID = a.SomeFkID and SomeFkStatus = 303
order by SomeFkDatetime desc
) f303


-- option 3: one OUTER APPLY for the last status and Subquery (select top 1 order by) for the 303 status column
SELECT a.ID, a.SomeFkID, 
( select top 1 SomeFkDatetime
from TableFk where SomeFkID = a.SomeFkID and SomeFkStatus = 303
order by SomeFkDatetime desc
) AS YatzranStatusDate, 
f.SomeFkDesc AS ErrorDesc,
f.SomeFkStatus as FileStatusID,
f.SomeFkDatetime
FROM TableA a
OUTER APPLY
( select top 1 SomeFkID, SomeFkStatus, SomeFkDatetime, SomeFkDesc
from TableFk where SomeFkID = a.SomeFkID
order by SomeFkDatetime desc
) f

-- option 4: one OUTER APPLY for the last status and Subquery (max(SomeFkDatetime)) for the 303 status column
SELECT a.ID, a.SomeFkID, 
( select max(SomeFkDatetime)
from TableFk where SomeFkID = a.SomeFkID and SomeFkStatus = 303
) AS YatzranStatusDate, 
f.SomeFkDesc AS ErrorDesc,
f.SomeFkStatus as FileStatusID,
f.SomeFkDatetime
FROM TableA a
OUTER APPLY
( select top 1 SomeFkID, SomeFkStatus, SomeFkDatetime, SomeFkDesc
from TableFk where SomeFkID = a.SomeFkID
order by SomeFkDatetime desc
) f


-- option 4: FIRST_VALUE and MAX functions:
SELECT DISTINCT
a.ID, a.SomeFkID, 
MAX (case when f.SomeFkStatus = 303 then f.SomeFkDatetime else NULL end) OVER (PARTITION BY f.SomeFkID) AS YatzranStatusDate,
FIRST_VALUE(f.SomeFkDesc) OVER (PARTITION BY f.SomeFkID ORDER BY f.SomeFkDatetime desc) AS ErrorDesc,
FIRST_VALUE(f.SomeFkStatus) OVER (PARTITION BY f.SomeFkID ORDER BY f.SomeFkDatetime desc) as FileStatusID,
FIRST_VALUE(f.SomeFkDatetime) OVER (PARTITION BY f.SomeFkID ORDER BY f.SomeFkDatetime desc) as SomeFkDatetime
FROM TableA a
LEFT JOIN TableFk F ON A.SomeFkID  = f.SomeFkID



-- try it with and without :
CREATE NONCLUSTERED INDEX [IX_TableFk] ON TableFk
(
[SomeFkDatetime] DESC
INCLUDE ([SomeFkID])
ON [PRIMARY]


DROP TABLE TableA 
DROP TABLE TableFk 

No comments:

Post a Comment