Please start any new threads on our new site at https://forums.sqlteam.com. We've got lots of great SQL Server experts to answer whatever question you can come up with.

 All Forums
 Old Forums
 CLOSED - General SQL Server
 Multi-Functional Sproc

Author  Topic 

X002548
Not Just a Number

15586 Posts

Posted - 2003-05-19 : 13:32:25
It appears that I will be the lucky receipent of a transition of an application.

Access From End, SQL 2000 backend

This rocket scientist coded like:

SELECT TOP 100 PERCENT Col1, col2 FROM Table1 LEFT JOIN Table2
SELECT col1, col2, col3 FROM Table GROUP BY col1, col2, col3
17 Table Joins

The list goes on.

Now the question.

There are 5 SPROCS, but each 1 has over a hundred "function" calls. In other words, the Access front makes 1 call to s sproc, but passes in which function needs to be executed (along with parameters.

I don't understand how the damn things runs or stays afloat.

Isn't this a serious design flaw?

(Not that I plan on opening the damn thing unless I have too).



Brett

8-)

tkizer
Almighty SQL Goddess

38200 Posts

Posted - 2003-05-19 : 13:45:06
Hey Jeff, where'd your reply go? Decide to delete it?

Tara
Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2003-05-19 : 13:55:59
What did he say?



Brett

8-)
Go to Top of Page

tkizer
Almighty SQL Goddess

38200 Posts

Posted - 2003-05-19 : 13:57:20
He gave explanations as to why the rocket scientist could have coded that way.

Tara
Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2003-05-19 : 14:09:18
Well I'm sure he did.

Mostly it appears that this developer decided to take an existing poorly design access database and port it.

You know how queries are usually called from queries? I've seen levels as deep as 12 with many branchs. Why? Because:

Here's a sample of what I'm looking at. (Notice the Group By, there's no aggregation or scalar function in the SELECT..he really wanted a distinct row...and how do you do that in Access if you don't know the keyword DISTINCT...)



SELECT tblCaseTrackerUnique.CompletedBy,
[FirstName] + ' ' + [LastName] AS Handler,
tblLkUpTLCs.TeamLeader,
qryCaseTrackerPrivacyCardByUser.CountOfRecordID AS TotalHandled,
qryCaseTrackerPrivacyCardPending.CountOfRecordID AS Pending,
qryCaseTrackerPrivacyCardComplete.CountOfRecordID AS Completed,
qryCaseTrackerPrivacyCardPendingQR.CountOfRecordID AS PendingQR,
qryCaseTrackerPrivacyCardNotToBeQRd.CountOfRecordID AS NotToBeQRd,
qryCaseTrackerPrivacyCardBypass.CountOfRecordID AS Bypass,
qryCaseTrackerPrivacyCardCorrect.CountOfRecordID AS Correct,
qryCaseTrackerPrivacyCardIncorrect.CountOfRecordID AS Incorrect

FROM ((((((((tblCaseTrackerUnique WITH (NOLOCK)
LEFT OUTER JOIN (tblNames LEFT OUTER JOIN tblLkUpTLCs ON tblNames.TLCsID = tblLkUpTLCs.TLCsID) ON tblCaseTrackerUnique.CompletedBy = tblNames.IonsID)

-- qryCaseTrackerUniqueCaseIDByUser
LEFT OUTER JOIN
(
SELECT qryCaseTrackerUniqueCaseIDByUser.CompletedBy,

Count(tblCaseTrackerAddInfo.RecordID) AS CountOfRecordID
FROM tblCaseTrackerAddInfo WITH (NOLOCK) INNER JOIN
(
SELECT tblCaseTrackerUnique.*
FROM tblCaseTrackerUnique WITH (NOLOCK)
WHERE ((tblCaseTrackerUnique.ProjectID)=@ProjectID)
) as qryCaseTrackerUniqueCaseIDByUser
ON tblCaseTrackerAddInfo.ProjectID=qryCaseTrackerUniqueCaseIDByUser.ProjectID
AND tblCaseTrackerAddInfo.CaseID=qryCaseTrackerUniqueCaseIDByUser.CaseID
WHERE (((tblCaseTrackerAddInfo.ProjectID)=30) AND (tblCaseTrackerAddInfo.NumberG<>0) --added
AND ((tblCaseTrackerAddInfo.CreatedDate) Between @DateStart And @DateEnd))
GROUP BY qryCaseTrackerUniqueCaseIDByUser.CompletedBy
)
AS qryCaseTrackerPrivacyCardByUser ON tblCaseTrackerUnique.CompletedBy = qryCaseTrackerPrivacyCardByUser.CompletedBy)

LEFT OUTER JOIN
-- qryCaseTrackerPrivacyCardPending
(
SELECT qryCaseTrackerPrivacyCardPending.CompletedBy,
Count(tblCaseTrackerAddInfo.RecordID) As CountOfRecordID
FROM tblCaseTrackerAddInfo WITH (NOLOCK) INNER JOIN
(
SELECT tblCaseTrackerUnique.*
FROM tblCaseTrackerUnique WITH (NOLOCK)
WHERE ((tblCaseTrackerUnique.ProjectID)=@ProjectID)
) as qryCaseTrackerPrivacyCardPending
ON tblCaseTrackerAddInfo.ProjectID=qryCaseTrackerPrivacyCardPending.ProjectID
AND tblCaseTrackerAddInfo.CaseID=qryCaseTrackerPrivacyCardPending.CaseID
WHERE (((tblCaseTrackerAddInfo.ProjectID)=30) AND (tblCaseTrackerAddInfo.NumberG<>0)
AND ((tblCaseTrackerAddInfo.CreatedDate) Between @DateStart And @DateEnd))
AND tblCaseTrackerAddInfo.NumberE=2
AND tblCaseTrackerAddInfo.NumberJ=4
GROUP BY qryCaseTrackerPrivacyCardPending.CompletedBy
)
AS qryCaseTrackerPrivacyCardPending ON tblCaseTrackerUnique.CompletedBy = qryCaseTrackerPrivacyCardPending.CompletedBy)

LEFT OUTER JOIN
-- qryCaseTrackerPrivacyCardComplete
(
SELECT qryCaseTrackerPrivacyCardComplete.CompletedBy,
Count(tblCaseTrackerAddInfo.RecordID) As CountOfRecordID
FROM tblCaseTrackerAddInfo WITH (NOLOCK) INNER JOIN
(
SELECT tblCaseTrackerUnique.*
FROM tblCaseTrackerUnique WITH (NOLOCK)
WHERE ((tblCaseTrackerUnique.ProjectID)=@ProjectID)
) as qryCaseTrackerPrivacyCardComplete
ON tblCaseTrackerAddInfo.ProjectID=qryCaseTrackerPrivacyCardComplete.ProjectID
AND tblCaseTrackerAddInfo.CaseID=qryCaseTrackerPrivacyCardComplete.CaseID
WHERE ((((tblCaseTrackerAddInfo.ProjectID)=30) AND (tblCaseTrackerAddInfo.NumberG<>0)
AND ((tblCaseTrackerAddInfo.CreatedDate) Between @DateStart And @DateEnd))
AND tblCaseTrackerAddInfo.NumberE=2
AND tblCaseTrackerAddInfo.NumberJ<>4)
OR
((((tblCaseTrackerAddInfo.ProjectID)=30) AND (tblCaseTrackerAddInfo.NumberG<>0)
AND ((tblCaseTrackerAddInfo.CreatedDate) Between @DateStart And @DateEnd))
AND tblCaseTrackerAddInfo.NumberE<>2)
GROUP BY qryCaseTrackerPrivacyCardComplete.CompletedBy
)
AS qryCaseTrackerPrivacyCardComplete ON tblCaseTrackerUnique.CompletedBy = qryCaseTrackerPrivacyCardComplete.CompletedBy)

LEFT OUTER JOIN
--qryCaseTrackerPrivacyCardPendingQR
(
SELECT qryCaseTrackerPrivacyCardPendingQR.CompletedBy,
Count(tblCaseTrackerAddInfo.RecordID) As CountOfRecordID
FROM tblCaseTrackerAddInfo WITH (NOLOCK) INNER JOIN
(
SELECT tblCaseTrackerUnique.*
FROM tblCaseTrackerUnique WITH (NOLOCK)
WHERE ((tblCaseTrackerUnique.ProjectID)=@ProjectID)
) as qryCaseTrackerPrivacyCardPendingQR
ON tblCaseTrackerAddInfo.ProjectID=qryCaseTrackerPrivacyCardPendingQR.ProjectID
AND tblCaseTrackerAddInfo.CaseID=qryCaseTrackerPrivacyCardPendingQR.CaseID
WHERE (((tblCaseTrackerAddInfo.ProjectID)=30) AND (tblCaseTrackerAddInfo.NumberG<>0)
AND ((tblCaseTrackerAddInfo.CreatedDate) Between @DateStart And @DateEnd))
AND tblCaseTrackerAddInfo.NumberE=1
AND tblCaseTrackerAddInfo.NumberJ=4
AND qryCaseTrackerPrivacyCardPendingQR.QRdBy Is Null
GROUP BY qryCaseTrackerPrivacyCardPendingQR.CompletedBy
)
AS qryCaseTrackerPrivacyCardPendingQR ON tblCaseTrackerUnique.CompletedBy = qryCaseTrackerPrivacyCardPendingQR.CompletedBy)

LEFT OUTER JOIN
--qryCaseTrackerPrivacyCardNotToBeQRd
(
SELECT qryCaseTrackerPrivacyCardNotToBeQRd.CompletedBy,
Count(tblCaseTrackerAddInfo.RecordID) As CountOfRecordID
FROM tblCaseTrackerAddInfo WITH (NOLOCK) INNER JOIN
(
SELECT tblCaseTrackerUnique.*
FROM tblCaseTrackerUnique WITH (NOLOCK)
WHERE ((tblCaseTrackerUnique.ProjectID)=@ProjectID)
) as qryCaseTrackerPrivacyCardNotToBeQRd
ON tblCaseTrackerAddInfo.ProjectID=qryCaseTrackerPrivacyCardNotToBeQRd.ProjectID
AND tblCaseTrackerAddInfo.CaseID=qryCaseTrackerPrivacyCardNotToBeQRd.CaseID
WHERE (((tblCaseTrackerAddInfo.ProjectID)=30) AND (tblCaseTrackerAddInfo.NumberG<>0)
AND ((tblCaseTrackerAddInfo.CreatedDate) Between @DateStart And @DateEnd))
--AND (tblCaseTrackerAddInfo.NumberE=1 or tblCaseTrackerAddInfo.NumberE=2)
AND tblCaseTrackerAddInfo.NumberJ=4
AND qryCaseTrackerPrivacyCardNotToBeQRd.QRdBy Is Not Null
GROUP BY qryCaseTrackerPrivacyCardNotToBeQRd.CompletedBy
)
AS qryCaseTrackerPrivacyCardNotToBeQRd ON tblCaseTrackerUnique.CompletedBy = qryCaseTrackerPrivacyCardNotToBeQRd.CompletedBy)

LEFT OUTER JOIN
--qryCaseTrackerPrivacyCardBypass
(
SELECT qryCaseTrackerPrivacyCardBypass.CompletedBy,
Count(tblCaseTrackerAddInfo.RecordID) As CountOfRecordID
FROM tblCaseTrackerAddInfo WITH (NOLOCK) INNER JOIN
(
SELECT tblCaseTrackerUnique.*

FROM tblCaseTrackerUnique WITH (NOLOCK)
WHERE ((tblCaseTrackerUnique.ProjectID)=@ProjectID)
) as qryCaseTrackerPrivacyCardBypass
ON tblCaseTrackerAddInfo.ProjectID=qryCaseTrackerPrivacyCardBypass.ProjectID
AND tblCaseTrackerAddInfo.CaseID=qryCaseTrackerPrivacyCardBypass.CaseID
WHERE (((tblCaseTrackerAddInfo.ProjectID)=30) AND (tblCaseTrackerAddInfo.NumberG<>0)
AND ((tblCaseTrackerAddInfo.CreatedDate) Between @DateStart And @DateEnd))
AND tblCaseTrackerAddInfo.NumberJ=3
GROUP BY qryCaseTrackerPrivacyCardBypass.CompletedBy
)
AS qryCaseTrackerPrivacyCardBypass ON tblCaseTrackerUnique.CompletedBy = qryCaseTrackerPrivacyCardBypass.CompletedBy)

LEFT OUTER JOIN
--qryCaseTrackerPrivacyCardCorrect
(
SELECT qryCaseTrackerPrivacyCardCorrect.CompletedBy,
Count(tblCaseTrackerAddInfo.RecordID) As CountOfRecordID
FROM tblCaseTrackerAddInfo WITH (NOLOCK) INNER JOIN
(
SELECT tblCaseTrackerUnique.*
FROM tblCaseTrackerUnique WITH (NOLOCK)
WHERE ((tblCaseTrackerUnique.ProjectID)=@ProjectID)
) as qryCaseTrackerPrivacyCardCorrect
ON tblCaseTrackerAddInfo.ProjectID=qryCaseTrackerPrivacyCardCorrect.ProjectID
AND tblCaseTrackerAddInfo.CaseID=qryCaseTrackerPrivacyCardCorrect.CaseID
WHERE (((tblCaseTrackerAddInfo.ProjectID)=30) AND (tblCaseTrackerAddInfo.NumberG<>0)
AND ((tblCaseTrackerAddInfo.CreatedDate) Between @DateStart And @DateEnd))
AND tblCaseTrackerAddInfo.NumberJ=1
GROUP BY qryCaseTrackerPrivacyCardCorrect.CompletedBy
)
AS qryCaseTrackerPrivacyCardCorrect ON tblCaseTrackerUnique.CompletedBy = qryCaseTrackerPrivacyCardCorrect.CompletedBy)

LEFT OUTER JOIN
--qryCaseTrackerPrivacyCardIncorrect
(
SELECT qryCaseTrackerPrivacyCardIncorrect.CompletedBy,
Count(tblCaseTrackerAddInfo.RecordID) As CountOfRecordID
FROM tblCaseTrackerAddInfo WITH (NOLOCK) INNER JOIN
(
SELECT tblCaseTrackerUnique.*
FROM tblCaseTrackerUnique WITH (NOLOCK)
WHERE ((tblCaseTrackerUnique.ProjectID)=@ProjectID)
) as qryCaseTrackerPrivacyCardIncorrect
ON tblCaseTrackerAddInfo.ProjectID=qryCaseTrackerPrivacyCardIncorrect.ProjectID
AND tblCaseTrackerAddInfo.CaseID=qryCaseTrackerPrivacyCardIncorrect.CaseID
WHERE (((tblCaseTrackerAddInfo.ProjectID)=30) AND (tblCaseTrackerAddInfo.NumberG<>0)
AND ((tblCaseTrackerAddInfo.CreatedDate) Between @DateStart And @DateEnd))
AND tblCaseTrackerAddInfo.NumberJ=2
GROUP BY qryCaseTrackerPrivacyCardIncorrect.CompletedBy
)
AS qryCaseTrackerPrivacyCardIncorrect ON tblCaseTrackerUnique.CompletedBy = qryCaseTrackerPrivacyCardIncorrect.CompletedBy

WHERE (((qryCaseTrackerPrivacyCardByUser.CompletedBy) =@ID))

GROUP BY tblCaseTrackerUnique.CompletedBy,
[FirstName] + ' ' +[LastName], tblLkUpTLCs.TeamLeader,
qryCaseTrackerPrivacyCardByUser.CountOfRecordID,
qryCaseTrackerPrivacyCardPending.CountOfRecordID,
qryCaseTrackerPrivacyCardComplete.CountOfRecordID,
qryCaseTrackerPrivacyCardPendingQR.CountOfRecordID,
qryCaseTrackerPrivacyCardNotToBeQRd.CountOfRecordID,
qryCaseTrackerPrivacyCardBypass.CountOfRecordID,
qryCaseTrackerPrivacyCardCorrect.CountOfRecordID,
qryCaseTrackerPrivacyCardIncorrect.CountOfRecordID



Brett

8-)
Go to Top of Page

MichaelP
Jedi Yak

2489 Posts

Posted - 2003-05-19 : 15:20:38
I've seen some complicated things in my day, but OH MY GOD!@!@

Michael

<Yoda>Use the Search page you must. Find the answer you will.</Yoda>
Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2003-05-19 : 15:29:40
Thanks Mike...you feel my pain?

And it's more ignorant than complicated...some things just don't make sense( GROUP BY) and some are just down right dangerous (SELECT * in a derived table...puleeze).

Anyway, know anyone what a hundred function call sproc would be performance wise? At first I though it'd be bad, but if it keeps getting called won't it get stored in buffer space and be resident?

And if 2 calls to a sproc occur at the same time, aren't they "launching" their own virtual whatever? Or will there be contention?



Brett

8-)

Edited by - x002548 on 05/19/2003 15:30:55

Edited by - x002548 on 05/20/2003 10:04:15
Go to Top of Page

MichaelP
Jedi Yak

2489 Posts

Posted - 2003-05-19 : 15:43:55
I'm not sure I fully understand what that old coder did, but maybe you should make a stored proc that has the "function calls" in it and the function calls call a stored proc that takes care of whatever the function call should do.

Michael

<Yoda>Use the Search page you must. Find the answer you will.</Yoda>
Go to Top of Page

Arnold Fribble
Yak-finder General

1961 Posts

Posted - 2003-05-19 : 16:36:30
I got fed up drawing little boxes and arrows on the backs of envelopes trying to follow the queries in the last Access database I had dealings with: sat down and wrote something that (kind-of) works out what queries & tables get referenced by other queries, splats it into a GraphViz file and gets dot to draw the damned thing instead.


Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2003-05-20 : 10:02:53
Arnold,

I kind of follow but..

quote:

splats it into a GraphViz file and gets dot to draw the damned thing instead.




you lost me there...I'm guessing you wrote something (what) in vba/vb that interogates the collections...but the splats thing confuses me. Also how do you get a graphical representation (I'm assuming).

I've been screen shooting a documenting the layers.

Also any comment about a single stored procedure that has over a hundred uses? Pros/Cons of such an approach?

I think I'm getting this damn mess sometime in July...

After a "knowledge" transfer



Brett

8-)
Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2003-05-20 : 10:07:15
quote:

I'm not sure I fully understand what that old coder did, but maybe you should make a stored proc that has the "function calls" in it and the function calls call a stored proc that takes care of whatever the function call should do.



Thanks Mike, but there are 5 sprocs, each with 100 "functions". That would mean I'd have to recode 500 sprocs...no thanks...I've been give this "gift" as maint mode only...and that's where I'll leave it...

Just wondering when(if) perfromance will bottom out, in which case I'll have to do something...



Brett

8-)
Go to Top of Page

JimL
SQL Slinging Yak Ranger

1537 Posts

Posted - 2003-05-20 : 10:20:07
I have been Creating systems in access for years. But This............ I think Someone was getting paid buy the hour, and wanted to create somthing no one could work on. Ive seen this done before we Refer to it as a MUGWAMP devopment.

I will not give the translation for MUGWAMP as there may be more sensitive readers out there and it is not very nice.




Jim
Users <> Logic
Go to Top of Page

Arnold Fribble
Yak-finder General

1961 Posts

Posted - 2003-05-20 : 12:37:38
gotta run: http://www.research.att.com/sw/tools/graphviz/download.html
it's ugly and capricious, but it works!


Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2003-05-20 : 13:33:33
Just blew my mind with that, I'll keep looking at that site, but if you could back fill a little in your spare time, that'd be great.

Sounds like something I could definetly utilize.



Brett

8-)
Go to Top of Page

Arnold Fribble
Yak-finder General

1961 Posts

Posted - 2003-05-20 : 16:53:22
Brett,

Really I was picking up on your "You know how queries are usually called from queries? I've seen levels as deep as 12 with many branchs." comment. To be honest, I was probably not considering the SQL Server backend and your SP problems... sorry.
Anyways... here's a story
I'd already written a little Access database that interrogated the various collections in other Access databases and put the results into tables (not wholly dissimilar from the INFORMATION_SCHEMA views in SQL Server). I'd found this made it much easier to answer questions like what tables are not used as FKs, are there any duplicate indexes, and so on -- the sort of questions you have when someone dumps a complicated, undocumented database on your workdesk.
Anyway, I was finding it difficult to visualize what connected to what and how parts of the database clustered, both for table relationships and queries referencing other queries.
So to draw a pretty graph of what queries reference what...
There's a query that looks at the SQL for the queries and decides whether each table / query is used by that query:

PARAMETERS dbname Text;
INSERT INTO QueryTQRefs ( [Database], Query, type, object )
SELECT mQuery.Database, mQuery.Query, TableQueryUnion.type, TableQueryUnion.object
FROM TableQueryUnion INNER JOIN mQuery ON TableQueryUnion.database = mQuery.Database
WHERE (((mQuery.Database)=[dbname]) AND ((mQuery.SQL) Like ("*[!!.][[]" & [Object] & "]*")))
OR (((mQuery.Database)=[dbname]) AND ((TableQueryUnion.object) Not Like "*[- /+µ&()#]*")
AND ((mQuery.SQL) Like ("*[, (]" & [Object] & "[,; )]*")));

 
As you can see, there are some nasty heuristics in the Like expressions! Since they don't always work, it has a list of false references to delete from QueryTQRefs.

I think I used this to help somewhat, but I don't really remember where it came from!

SELECT O.Name AS Query, Q.Name1 AS Tab
FROM MSysObjects AS O
INNER JOIN MSysQueries AS Q ON O.Id = Q.ObjectId
WHERE Q.Attribute=5

 
Then it creates a text file that can be read by the GraphViz tools. Since the references between queries and tables are (one hopes!) acyclic, dot and dotty do a not-bad job, as long as things don't get too congested. It's probably possible to drive GraphViz directly through an API, but I didn't need anything that slick.
It outputs a node definition for each distinct table and query, which is just a line of text that gives the node an identifier (sadly, I found I had to assign artificial names to each node since some Access object names were illegal), a label (the name), a shape (I drew tables as rectangles, queries as ellipses) and a colour (I had different colours for select, append, insert, update, etc.).
It outputs an edge for each reference of a node in a query. The only attributes I used here were to have the result of action queries (make table, append, update, insert, delete) point 'upstream' and be a different colour.
Identifying the table affected by the action queries was done by this query:

SELECT O.Name AS Query, Q.Name1 AS Tab
FROM MSysObjects AS O
INNER JOIN MSysQueries AS Q ON O.Id = Q.ObjectId
WHERE Q.Attribute=1 AND Q.Flag IN (2,3)

 
Then it just starts dotty up with a Shell call.




Edited by - Arnold Fribble on 05/20/2003 16:56:23
Go to Top of Page

AjarnMark
SQL Slashing Gunting Master

3246 Posts

Posted - 2003-05-21 : 13:35:35
quote:
Thanks Mike, but there are 5 sprocs, each with 100 "functions". That would mean I'd have to recode 500 sprocs...no thanks...I've been give this "gift" as maint mode only...and that's where I'll leave it...

Just wondering when(if) perfromance will bottom out, in which case I'll have to do something...



Brett

8-)


Aw, gee, Brett, you get ALL the fun projects!

Not sure what would cause "performance to bottom out" other than perhaps some call that might trigger recursive calls to the sproc (such as calling another function inside the same sproc that calls another function, etc., etc.) Hopefully they don't do that. Other than that, I think it's mostly "just" a management and maintenance nightmare. But don't worry, I think hunting season for former employees and consultants opens in just a few weeks.

------------------------------------------------------
The more you know, the more you know you don't know.
Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2003-05-21 : 13:52:15
Arnold,

Thanks a bunch, esp for taking the time. It should be a big help.

Mark,

10 gauge or 12 gauge?

I'm seriously worried about this mess...

It'll be like:

"here you go...transition...BOOOOOOOOOOOOOOOOOOOOOOOOOOOM"



Brett

8-)
Go to Top of Page

AjarnMark
SQL Slashing Gunting Master

3246 Posts

Posted - 2003-05-21 : 14:17:46
quote:
Mark,

10 gauge or 12 gauge?

I'm seriously worried about this mess...

It'll be like:

"here you go...transition...BOOOOOOOOOOOOOOOOOOOOOOOOOOOM"


Use whatever you like, Brett. Personally, I kinda like poison-tipped darts from a blow gun, but my absolute favorite is the Gunting. It generates more pain and agony. And I don't want to scare you off here, but have you noticed how many of the folks who have posted more than 1000 times here have gone through a "transition" in their place of work in the last year?

------------------------------------------------------
The more you know, the more you know you don't know.
Go to Top of Page
   

- Advertisement -