473,788 Members | 2,706 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Want to make ''Get Latest Status'' function more efficient

Hey folks, I'm looking at making the following query more efficient
potentially using the ranking functions and I'd like some advice from
the gurus. The purpose of the following is to have a status for a
person, and also have a historical background as to what they've done,
status wise. This was the best way I could come up with to do this a
few years back, but I'm wondering if there's a better way with Sql
2005.

Here's a toned down structure and my query. Any help/critique would
be greatly appreciated.
CREATE TABLE #Status(
StatusID int NOT NULL,
StatusName VARCHAR(50) NOT NULL,
StatusCategoryI D int NOT NULL
) ON [PRIMARY]

CREATE TABLE #RegStatus(
[RegistrationID] [uniqueidentifie r] NOT NULL,
[StatusID] [int] NOT NULL,
[StatusTimeStamp] [datetime] NOT NULL,
[UniqueRowID] [int] IDENTITY(1,1) NOT NULL
) ON [PRIMARY]

SET NOCOUNT on
INSERT INTO #Status VALUES(200, 'StatusA', 1)
INSERT INTO #Status VALUES(210, 'StatusB', 1)
INSERT INTO #Status VALUES(115, 'StatusC', 1)
INSERT INTO #Status VALUES(112, 'StatusD', 1)
INSERT INTO #Status VALUES(314, 'StatusE', 1)
INSERT INTO #Status VALUES(15, 'StatusF', 1)
INSERT INTO #Status VALUES(22, 'StatusG', 1)

INSERT INTO #Status VALUES(300, 'StatusX', 2)
INSERT INTO #Status VALUES(310, 'StatusY', 2)
INSERT INTO #Status VALUES(320, 'StatusZ', 2)

INSERT INTO #RegStatus VALUES('7A6058D 0-06CB-4E83-A8C4-B1AFC74B11F0',
200, GETDATE())
INSERT INTO #RegStatus VALUES('7A6058D 0-06CB-4E83-A8C4-B1AFC74B11F0',
210, GETDATE())
INSERT INTO #RegStatus VALUES('7A6058D 0-06CB-4E83-A8C4-B1AFC74B11F0',
115, GETDATE())

INSERT INTO #RegStatus VALUES('8B94A66 6-A3DD-4CB1-89A0-9910047AE7A0',
112, GETDATE())
INSERT INTO #RegStatus VALUES('8B94A66 6-A3DD-4CB1-89A0-9910047AE7A0',
314, GETDATE())
INSERT INTO #RegStatus VALUES('8B94A66 6-A3DD-4CB1-89A0-9910047AE7A0',
200, GETDATE())
INSERT INTO #RegStatus VALUES('8B94A66 6-A3DD-4CB1-89A0-9910047AE7A0',
22, GETDATE())

INSERT INTO #RegStatus VALUES('58AF0FC 6-C900-4BD0-B3F7-F9D62701F021',
15, GETDATE())
INSERT INTO #RegStatus VALUES('58AF0FC 6-C900-4BD0-B3F7-F9D62701F021',
115, GETDATE())
INSERT INTO #RegStatus VALUES('58AF0FC 6-C900-4BD0-B3F7-F9D62701F021',
200, GETDATE())
INSERT INTO #RegStatus VALUES('58AF0FC 6-C900-4BD0-B3F7-F9D62701F021',
115, GETDATE())
SET NOCOUNT Off

/*
This is a query from within a function that I use to not only get the
latest
status for one registrant, but I can use it to get the latest status
for everyone as well.
*/
DECLARE @RegStatusCatID int,
@RegID UNIQUEIDENTIFIE R

SET @RegStatusCatID = 1
SET @RegID = null

select LS.*, S.StatusName, S.StatusCategor yID
from #Status S
join(
select RS.Registration ID, RS.StatusID, RS.StatusTimeSt amp
from #RegStatus RS
join
(
SELECT RS.Registration ID , max(RS.UniqueRo wID) UniqueRowID
FROM #RegStatus RS
join #Status S
on RS.StatusID = S.StatusID
and S.StatusCategor yID = @RegStatusCatID
and (
@RegID is null
or (@RegID is not null
and RS.Registration ID = @RegID)
)
group by RS.Registration ID
)LS
on RS.UniqueRowID = LS.UniqueRowID
) LS
on S.StatusID = LS.StatusID

--SELECT * FROM #RegStatus

DROP TABLE #RegStatus
DROP TABLE #Status

May 14 '07 #1
4 2090
On May 14, 10:02 am, Mark <mark.ru...@gma il.comwrote:
Hey folks, I'm looking at making the following query more efficient
potentially using the ranking functions and I'd like some advice from
the gurus. The purpose of the following is to have a status for a
person, and also have a historical background as to what they've done,
status wise. This was the best way I could come up with to do this a
few years back, but I'm wondering if there's a better way with Sql
2005.

Here's a toned down structure and my query. Any help/critique would
be greatly appreciated.

CREATE TABLE #Status(
StatusID int NOT NULL,
StatusName VARCHAR(50) NOT NULL,
StatusCategoryI D int NOT NULL
) ON [PRIMARY]

CREATE TABLE #RegStatus(
[RegistrationID] [uniqueidentifie r] NOT NULL,
[StatusID] [int] NOT NULL,
[StatusTimeStamp] [datetime] NOT NULL,
[UniqueRowID] [int] IDENTITY(1,1) NOT NULL
) ON [PRIMARY]

SET NOCOUNT on
INSERT INTO #Status VALUES(200, 'StatusA', 1)
INSERT INTO #Status VALUES(210, 'StatusB', 1)
INSERT INTO #Status VALUES(115, 'StatusC', 1)
INSERT INTO #Status VALUES(112, 'StatusD', 1)
INSERT INTO #Status VALUES(314, 'StatusE', 1)
INSERT INTO #Status VALUES(15, 'StatusF', 1)
INSERT INTO #Status VALUES(22, 'StatusG', 1)

INSERT INTO #Status VALUES(300, 'StatusX', 2)
INSERT INTO #Status VALUES(310, 'StatusY', 2)
INSERT INTO #Status VALUES(320, 'StatusZ', 2)

INSERT INTO #RegStatus VALUES('7A6058D 0-06CB-4E83-A8C4-B1AFC74B11F0',
200, GETDATE())
INSERT INTO #RegStatus VALUES('7A6058D 0-06CB-4E83-A8C4-B1AFC74B11F0',
210, GETDATE())
INSERT INTO #RegStatus VALUES('7A6058D 0-06CB-4E83-A8C4-B1AFC74B11F0',
115, GETDATE())

INSERT INTO #RegStatus VALUES('8B94A66 6-A3DD-4CB1-89A0-9910047AE7A0',
112, GETDATE())
INSERT INTO #RegStatus VALUES('8B94A66 6-A3DD-4CB1-89A0-9910047AE7A0',
314, GETDATE())
INSERT INTO #RegStatus VALUES('8B94A66 6-A3DD-4CB1-89A0-9910047AE7A0',
200, GETDATE())
INSERT INTO #RegStatus VALUES('8B94A66 6-A3DD-4CB1-89A0-9910047AE7A0',
22, GETDATE())

INSERT INTO #RegStatus VALUES('58AF0FC 6-C900-4BD0-B3F7-F9D62701F021',
15, GETDATE())
INSERT INTO #RegStatus VALUES('58AF0FC 6-C900-4BD0-B3F7-F9D62701F021',
115, GETDATE())
INSERT INTO #RegStatus VALUES('58AF0FC 6-C900-4BD0-B3F7-F9D62701F021',
200, GETDATE())
INSERT INTO #RegStatus VALUES('58AF0FC 6-C900-4BD0-B3F7-F9D62701F021',
115, GETDATE())
SET NOCOUNT Off

/*
This is a query from within a function that I use to not only get the
latest
status for one registrant, but I can use it to get the latest status
for everyone as well.
*/

DECLARE @RegStatusCatID int,
@RegID UNIQUEIDENTIFIE R

SET @RegStatusCatID = 1
SET @RegID = null

select LS.*, S.StatusName, S.StatusCategor yID
from #Status S
join(
select RS.Registration ID, RS.StatusID, RS.StatusTimeSt amp
from #RegStatus RS
join
(
SELECT RS.Registration ID , max(RS.UniqueRo wID) UniqueRowID
FROM #RegStatus RS
join #Status S
on RS.StatusID = S.StatusID
and S.StatusCategor yID = @RegStatusCatID
and (
@RegID is null
or (@RegID is not null
and RS.Registration ID = @RegID)
)
group by RS.Registration ID
)LS
on RS.UniqueRowID = LS.UniqueRowID
) LS
on S.StatusID = LS.StatusID

--SELECT * FROM #RegStatus

DROP TABLE #RegStatus
DROP TABLE #Status
Read in BOL about
ROW_NUMBER() OVER(PARTITION BY RegistrationID ORDER BY StatusTimeStamp
DESC) = 1

May 14 '07 #2
As Alex suggested, you can use the ROW_NUMBER function in SQL Server 2005.
Here is one solution that does that:

WITH Registrations AS
(
SELECT RegistrationID,
StatusID,
StatusTimeStamp ,
ROW_NUMBER() OVER(
PARTITION BY RegistrationID
ORDER BY StatusTimeStamp DESC,
StatusID) AS rn
FROM #RegStatus
WHERE RegistrationID = COALESCE(@RegID , RegistrationID)
)
SELECT R.RegistrationI D,
S.StatusID,
R.StatusTimeSta mp,
S.StatusName,
S.StatusCategor yID
FROM #Status AS S
INNER JOIN Registrations AS R
ON S.StatusID = R.StatusID
WHERE StatusCategoryI D = @RegStatusCatID
AND rn = 1;

A few notes:

- If you decide to use this approach, the ROW_NUMBER function can benefit
from index created on the partitioning column and the sort columns. In this
case would be (RegistrationID , StatusTimeStamp , StatusID).
- The way you created your example, it generates duplicate StatusTimeStamp
dates for a registrant. For that reason I added StatusID in the ORDER BY
clause of ROW_NUMBER to return deterministic result. However, the result may
or may not match your original solution. That is because you had the
IDENTITY column which was an indicator of the latest inserted row. Here I
used the StatusTimeStamp as an indicator for the latest status. If you
expect to have duplicate dates and the StatusID is not good for a
tiebreaker, then I guess using the IDENTITY would be the only solution.
- I assumed the goal is to avoid using the IDENTITY column. If not, then
using ROW_NUMBER with ORDER BY UniqueRowID DESC will provide deterministic
result.

HTH,

Plamen Ratchev
http://www.SQLStudio.com
May 14 '07 #3
Alex and Plamen, thanks so much for the help.

I'm using a UDF to return this data, so I've opted out of using the
Common Table Expression. The Row_Number helped tons. Over the test
data (not much) I've improved efficiency quite a bit, according to the
execution plans. I'm going to run this on live data soon, to see how
it works. I've changed my query to be as follows;

SELECT *
FROM #Status S
JOIN
(
SELECT RegistrationID,
StatusID,
StatusTimeStamp ,
[UniqueRowID],
ROW_NUMBER()
OVER(
PARTITION BY RegistrationID
ORDER BY [UniqueRowID] DESC
) AS rn
FROM #RegStatus RS
where (
@RegID is null
or (@RegID is not null
and RS.Registration ID = @RegID)
)
) RS
ON S.StatusID = RS.StatusID
AND RS.RN = 1
AND S.StatusCategor yID = @RegStatusCatID

Thanks Again!

Mark

May 14 '07 #4
Everything you are doing is wrong. Please read any book on data
modeling and the ISO-11179 naming rules. There is no such attribute
as a magical, universal "status" in a valid data model; it has to be
some kind of status -- "shipping_statu s", "martial_status ",
"employment_sta tus", etc.

Monsters postfixes like "_status_cat_id " are really silly! What is
it? A status code, a category? or an identifier? By definition it
cannot be all of those things at once. Hey why not go all out and
have a "_status_cat_id _value_name_tim e" postfix!

You then load tables with UNIQUEIDENTIFIE R and IDENTITY(1,1); but at
least you know that these are for non-relational physical locators,
like sequentially numbering rows to get a fake magnetic tape and not
even pretend that you are doing RDBMS design.
>>. The purpose of the following is to have a status for a person, and also have a historical background as to what they've done, status wise. <<
The usual design error is to have only one time in a row to capture
when an event started, then do horrible self-joins to get the duration
of the status change. Let me use a history table for price changes.
The fact to store is that a price had a duration:

CREATE TABLE PriceHistory
(upc CHAR(13) NOT NULL
REFERENCES Inventory(upc),
start_date DATE NOT NULL,
end_date DATE, -- null means current
CHECK(start_dat e < end_date),
PRIMARY KEY (upc, start_date),
item_price DECIMAL (12,4) NOT NULL
CHECK (item_price 0.0000),
etc.);

You actually needs more checks to assure that the start date is at
00:00 and the end dates is at 23:59:59.999 Hrs. You then use a
BETWEEN predicate to get the appropriate price.

SELECT ..
FROM PriceHistory AS H, Orders AS O
WHERE O.sales_date BETWEEN H.start_date
AND COALESCE (end_date, CURRENT_TIMESTA MP);

It is also a good idea to have a VIEW with the current data:

CREATE VIEW CurrentPrices (..)
AS
SELECT ..
FROM PriceHistory
WHERE end_date IS NULL;

There is a common kludge to repair a failure to design a history table
properly that you can put in a VIEW if you are not able to set things
right. Assume that every day we take a short inventory and put it in
a journal. The journal is a clip board paper form that has one line
per item per day, perhaps with gaps in the data. We want to get this
into the proper format, namely periods shown with a (start_date,
end_date) pair for durations where each item had the same quantity on
hand. This is due to Alejandro Mesa

CREATE TABLE InventoryJourna l
(journal_date DATETIME NOT NULL,
item_id CHAR(2) NOT NULL,
onhand_qty INTEGER NOT NULL);

WITH ItemGroups
AS
(SELECT journal_date, item_id, onhand_qty,
ROW_NUMBER() OVER(ORDER BY item_id, journal_date, onhand_qty)
- ROW_NUMBER() OVER(PARTITION BY item_id, onhand_qty
ORDER BY journal_date) AS item_grp_nbr
FROM Journal),
QtyByDateRanges
AS
(SELECT MIN(journal_dat e) AS start_date,
MAX(journal_dat e) AS end_date,
item_id, onhand_qty
FROM ItemGroups
GROUP BY item_id, onhand_qty, item_grp_nbr)

SELECT start_date, end_date, item_id, onhand_qty
FROM QtyByDateRanges ;

This might be easier to see with some data and intermediate steps

INSERT INTO InventoryJourna l VALUES('2007-01-01', 'AA', 100);
INSERT INTO InventoryJourna l VALUES('2007-01-01', 'BB', 200);
INSERT INTO InventoryJourna l VALUES('2007-01-02', 'AA', 100);
INSERT INTO InventoryJourna l VALUES('2007-01-02', 'BB', 200);
INSERT INTO InventoryJourna l VALUES('2007-01-03', 'AA', 100);
INSERT INTO InventoryJourna l VALUES('2007-01-03', 'BB', 300);

start_date end_date item_id onhand_qty
=============== =============== ============
'2007-01-01' '2007-01-03' 'AA' 100
'2007-01-01' '2007-01-02' 'BB' 200
'2007-01-03' '2007-01-03' 'BB' 300

Now, download the Rick Snodgrass book on Temporal Queries in SQL from
the University of Arizona website (it is free). And finally Google up
my article at www.DBAzine.com on transition constraints.

May 15 '07 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

354
15927
by: Montrose... | last post by:
After working in c# for a year, the only conclusion I can come to is that I wish I knew c. All I need is Linux, the gnu c compiler and I can do anything. Web services are just open sockets hooked up to interfaces. The Gtk is more than enough gui.
9
2342
by: Gianni Mariani | last post by:
I'm involved in a new project and a new member on the team has voiced a strong opinion that we should utilize exceptions. The other members on the team indicate that they have either been burned with unmaintainable code (an so are now not using exceptions). My position is that "I can be convinced to use exceptions" and my experience was that it let to code that was (much) more difficult to debug. The team decided that we'd give...
2
2129
by: seash | last post by:
hi I wrote a class like this.......in my application. public class cls { public static extern bool myApp ( ref string aa,
182
7560
by: Jim Hubbard | last post by:
http://www.eweek.com/article2/0,1759,1774642,00.asp
18
4154
by: mistral | last post by:
Is there some other (more advanced) effects for status bar, other than standard Scroller Bar, TypeWriter Scroller, Flashing Bar, Decrypter, Ticker, World Clock?
33
15583
by: Michael B Allen | last post by:
Hello, Early on I decided that all text (what most people call "strings" ) in my code would be unsigned char *. The reasoning is that the elements of these arrays are decidedly not signed. In fact, they may not even represent complete characters. At this point I think of text as simple binary blobs. What charset, character encoding and termination they use should not be exposed in the interface used to operate on them. But now I have...
21
2068
by: jehugaleahsa | last post by:
Hello: I had an hour-long discussion with my boss today. Last night, right before I dozed off, I realized some of his code resulted in duplicate processing. I tried to explain it to him and he kept saying, "I'm afraid to say it's 'OK' to change it because everything I did was for a reason." Well, I know better. My boss' old code is written in some cryptic C file with Oracle precompiler macros barfed all over it. Once I figured out how...
12
2014
by: Atropo | last post by:
Hi all. Having several strings how do i combine them to construct a command; lets say to run the date command. string str = "14/10/08 19:06:09"; strDD = str.substr(0,2); strMM = str.substr(3,2); strAA = str.substr(6,2); strhh = str.substr(9,2);
1
2488
by: joshai | last post by:
Hi, I'm pretty new to the php/mysql world and am building an article database for a website with multiple content types. I have an entry screen built that allows the site owner to enter articles, set article types, and set articles as features. Each page in the site calls the database for a feature article for that pages content type, then calls again for the 5 newest articles of that content type excluding any article that is identified as a...
0
9656
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
10370
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
10177
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
0
9969
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
8995
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing, and deployment—without human intervention. Imagine an AI that can take a project description, break it down, write the code, debug it, and then launch it, all on its own.... Now, this would greatly impact the work of software developers. The idea...
1
7519
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5402
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
5538
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
2
3677
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.