Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations wOOdy-Soft on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Suggestions as well as help... 1

Status
Not open for further replies.

fattyfatpants

Programmer
Apr 22, 2004
68
US
Platform: SQL Server 2000

I work for a CPA firm and the managers and Partners want to know that their staff are not only working a desired amount of hours but that the hours they are working are considered billable time.

The following Stored Procedure tries to perform an hours analysis for Curren Month of Current Year, Current Month of Prior Year, Current Year To Date, Prior Year To Date, and 2 Years Prior Year To Date.

Code:
CREATE PROCEDURE sp_test
	@Year varchar(4), --will hold the fiscal year wanted
	@CurrentMonth varchar(2) --will hold the current month in fiscal year

 AS

Declare @CurrentYrMonth varchar(4), --will hold the current month and two digit fiscal year
	@PriorYrMonth varchar(4), --will hold the current month and two digit fiscal year of the prior year
	@CurrentYr varchar(2), --will hold the two digit value of the current fiscal year
	@PriorYr varchar(2), --will hold the prior two digit fiscal year
	@TwoPriorYr varchar(2), --will hold the two digit fiscal year for 2 years ago
	@CurrentYrToDate varchar(85), --will hold the current fiscal year up to date so if I want to see from 0604 to 0904 it will hold 0604, 0704, 0804, 0904
	@PriorYrToDate varchar(85), --will hold the previous fiscal year up to date 
	@TwoPriorYrToDate varchar(85), --will hold the fiscal year two years ago up to date
	@NumMonths int, --will be used for the YTD computations, will calculate the number of months from June to current month
	@DefaultMonth varchar(2), --will be used as the base for the fiscal year, default to 06 for June
	@LoopCount int --will be used as incrementer in the While loop

Set @CurrentYr = RIGHT(@Year, 2) --Ex. 04 for 2004 if @Year = 2004
Set @PriorYr = RIGHT(CAST(CAST(@Year AS int) -1) AS varchar(2)), 2) --Ex. 03 for 2003 if @Year = 2004
Set @TwoPriorYr = RIGHT(CAST(CAST(@Year AS int) - 2) AS varchar(2)), 2) --Ex. 02 for 2002 if @Year = 2004

Set @CurrentYrMonth = @CurrentMonth + @CurrentYr --Ex. 0904 if @CurrentMonth = 09 and @CurrentYr = 04
Set @PriorYrMonth = @CurrentMonth + @PriorYr --Ex. 0903 if @CurrentMonth = 09 and @PriorYr = 03

Set @DefaultMonth = 06 --be sure to start with June since it is the beginning of the fiscal year

Set @LoopCount = 1 --set the value of the loop
Set @NumMonths = CAST(@CurrentMonth AS int) - 6  --Ex. if we want year to date and this month is 0904 we want to have 0604, 0704, 0804, 0904 included in the query so @NumMonths = 3

Set @CurrentYrToDate = @DefaultMonth + @CurrentYr --Ex. will hold 0604 if @DefaultMonth holds 06 and @CurrentYr holds 04
Set @PriorYrToDate = @DefaultMonth + @PriorYr --Ex. will hold 0603 if @DefaultMonth holds 06 and @PriorYr holds 03
Set @TwoPriorYrToDate = @DefaultMonth + @TwoPriorYr --Ex. will hold 0602 if @Default holds 06 and @TwoPriorYr holds 02

--While loop is used to build the strings for the queries
WHILE @LoopCount <= @NumMonths
	BEGIN
		
		Set @CurrentYrToDate = @CurrentYrToDate + ', ' + (@DefaultMonth + @LoopCount) + @CurrentYr
		Set @PriorYrToDate = @PriorYrToDate + ', ' + (@DefaultMonth + @LoopCount) + @PriorYr
		Set @TwoPriorYrToDate = @TwoPriorYrToDate + ', ' + (@DefaultMonth + @LoopCount) + @TwoPriorYr
		
		Set @LoopCount = @LoopCount + 1

	END

/*PRINT @CurrentYrToDate
PRINT @PriorYrToDate
PRINT @TwoPriorYrToDate*/

SELECT SUM(timewahs.thhrwkdw) CurrentChargeable, SUM(timewahs.thhrwkdw) + SUM(timewahs.thhrnbdw) as CurrentTotal, tkdept
  FROM timekeep INNER JOIN timewahs ON timekeep.tkinit = timewahs.thtk
 WHERE thper = @CurrentYrMonth
 GROUP BY tkdept

SELECT SUM(timewahs.thhrwkdw) PriorChargeable, SUM(timewahs.thhrwkdw) + SUM(timewahs.thhrnbdw) as PriorTotal, tkdept
  FROM timekeep INNER JOIN timewahs ON timekeep.tkinit = timewahs.thtk
 WHERE thper = @PriorYrMonth
 GROUP BY tkdept

SELECT SUM(timewahs.thhrwkdw) YTDChargeable, SUM(timewahs.thhrwkdw) + SUM(timewahs.thhrnbdw) as YTDTotal, tkdept
  FROM timekeep INNER JOIN timewahs ON timekeep.tkinit = timewahs.thtk
 WHERE thper IN (@CurrentYrToDate)
 GROUP BY tkdept

SELECT SUM(timewahs.thhrwkdw) PriorYTDChargeable, SUM(timewahs.thhrwkdw) + SUM(timewahs.thhrnbdw) as PriorYTDTotal, tkdept
  FROM timekeep INNER JOIN timewahs ON timekeep.tkinit = timewahs.thtk
 WHERE thper IN (@PriorYrToDate)
 GROUP BY tkdept

SELECT SUM(timewahs.thhrwkdw) TwoYTDChargeable, SUM(timewahs.thhrwkdw) + SUM(timewahs.thhrnbdw) as TwoYTDTotal, tkdept
  FROM timekeep INNER JOIN timewahs ON timekeep.tkinit = timewahs.thtk
 WHERE thper IN (@TwoPriorYrToDate)
 GROUP BY tkdept

First of all, it doesn't like my CAST statements up near the top on @PriorYr and @TwoPriorYr.

Second, I plan on using all of this in a single Crystal Report (CR version 9) as opposed to multiple subreports so should I use a UNION statement between all of the SELECT statements or how could I go about doing it?

Lastly, could you offer me any improvements on the structure of the procedure?

Thanks a million!!!
 
YOu are missing a parenthesis in each of the cast statments
Set @PriorYr =CAST((CAST(@Year AS int) -1) AS varchar(2)) --Ex. 03 for 2003 if @Year = 2004
Set @TwoPriorYr = RIGHT(CAST((CAST(@Year AS int) - 2) AS varchar(2)), 2)

Questions about posting. See faq183-874
 
You are headed for trouble. Ignoring other problems with the actual code, start with a database redesign. Don't use varchar where you should be using datetime or int.
-Karl

[red] Cursors, triggers, user-defined functions and dynamic SQL are an axis of evil![/red]
[green]Life's uncertain...eat dessert first...www.deerfieldbakery.com[/green]
 
SQLSister - Thanks for the help on that, works like a charm.

Donutman - Unfortunately I have no control over the DB design, this thing is absolutely huge (over 1000 tables) and is extremely difficult to work with. I can only come to the conclusion that the DB designer(s) wanted job security. When you think that things should be datetime they are varchar, believe me this is very annoying and makes generating any kind of date driven report a very tedious task at best.

Can either of you (or anyone else) think of anything else I can do to optimize the procedure?
 
Oh yeah, yes you can union then together, just make sure that you alias the fields in the first select to whatever you want the column names to be in the final report. Do you need to add a column to indicate which of these types of summary it is?

Questions about posting. See faq183-874
 
Do you need to add a column to indicate which of these types of summary it is?

Yes I do need to do that, would you be willing to help me out?? Also, do you know how to pad text?? I would like to check to make sure that @CurrentYr, @PriorYr, @TwoPriorYr, and @DefaultMonth are always 2 characters...it seems that the leading zero gets stripped off and messes me up in my While loop...Thanks again :)
 
I would venture to bet that it would be more efficient to create another table that stores the numeric data appropriately. Then add triggers to keep the redundant data consistent. And slowly wean yourself away from all of the varchar data. If you don't like triggers, you could just rebuild the new table every time you wanted to.
What do you think, SQLSister, wouldn't that be better than doing all that string manipulation?
-Karl

[red] Cursors, triggers, user-defined functions and dynamic SQL are an axis of evil![/red]
[green]Life's uncertain...eat dessert first...www.deerfieldbakery.com[/green]
 
Donutman, that might work out more efficiently. Could depend on how much it slows data entry though. If records are entered one at a time, it would probably be a good idea, but if they come in through bulk inserts, they might slow down too much. At least it would be a start on fixing the problems and make future development and maintenance fixes have a chance to be more efficient. Have to do it a little at a time though, 1000 tables is pretty big for making large scale design changes. Maybe start with the tables that are most frequently accessed. Might also depend on how the code is written too, if there are lot of union queries using select * for intance adding new fields might break them.

I've worked with some of the largest governemtn agencies there are and done db work for over 20 years and I don;t think I've run into any database with that many tables. Can't imagine what an accounting firm would be doing to need so many.

Questions about posting. See faq183-874
 
They're probably client tables rather than relationship driven tables. Another questionable design choice, but I could buy that one much more so than the varchar stuff.
-Karl
 
Yeah but if they are client tables the Union using Select * is a stronger possibility. That's why I mentioned it. YOu know this brings up an interesting discussion topic for the general database forum. At what point to do you decide that a production database needs a complete restructuring and how do you convince management. I think I'll go do that.

Questions about posting. See faq183-874
 
Thanks a lot for the responses...I'm not sure if either of you have heard of Elite; it is an ERP system that, to put bluntly, sucks. The documentation is horrible, the table naming convenstions seem like they were made up by someone having a seizure...Nothing about this system is logical, and because of the sheer size of the database I do not dare to make even a single change out of fear that table 1013 relates to table 4, table 96, 150, and 926 and in making the single change breaks the entire system. I have also heard that not even the developers of this system know what everything does.

I'm not sure how I would get all of the financial info into a temp table without doing all of the string manipulation. If someone enters 1104 as a period, I need to look at the month portion to see if it is >= 6 and <=12, if it is then the fiscal year is 2005. Then I need to calculate the number of months from 0604 to 1104 to build a string for use in an IN statement (WHERE period IN ('0604', '0704', '0804', '0904', '1004', '1104')). Then based on that I need to create the exact same string but for prior year (WHERE period IN ('0603', '0703', '0803', '0903', '1003', '1103')) and 2 years prior (WHERE period IN ('0602', '0702', '0802', '0902', '1002', '1102')). And as I said above, I plan on using this in a Crystal Report (Version 9) so I am going to do a UNION statement between all of the SELECT statemements. Any help on designing it to use a temp table would be awesome. Thanks again
 
It is amazing to me the kind of drek people can sell as COTS prodcts.

Not sure what you mean about using a temp table? What do you intend to do with the temp table? You may be able to use a tbale variable depending onwhat you intend to do, these are generally more efficient.

But to create one you use create table and make sure to name it starting with #.

Then use an insert statement to insert the records into it.

Then use the temp table just exactly like any other table thorugh the rest of the code.

Questions about posting. See faq183-874
 
Ahh, I see, thanks...One more question; since I will be using a few IN statements, do I need to pad my variables with single quotes for it to work?? As it is now, when I print out the contents of my variables they look like this:

CurrenYrToDate: 0603, 0703, 0803, 0903, 1003, 1103, 1203, 0104, 0204, 0304, 0404, 0504
PrevYrToDate: 0602, 0702, 0802, 0902, 1002, 1102, 1202, 0103, 0203, 0303, 0403, 0503
TwoPrevYrToDate: 0601, 0701, 0801, 0901, 1001, 1101, 1201, 0102, 0202, 0302, 0402, 0502

And they don't work in the query
 
If you want to maintain the leading zeros, yes indeed these are some type of character data and need the single quotes.

Questions about posting. See faq183-874
 
Well you can't use @CurrenYrToDate that way. You should put each value into a temp table and use a join rather than an IN (@CurrenYrToDate) clause.
If you are writing new code than you have an opportunity to make changes. My suggestion is a way to do that without disturbing the current schema. You create new tables that contain the data with the proper datatype.
-Karl

[red] Cursors, triggers, user-defined functions and dynamic SQL are an axis of evil![/red]
[green]Life's uncertain...eat dessert first...www.deerfieldbakery.com[/green]
 
Could you maybe post some code on how I might do that...I would really like to learn from it. Also, when using an IN clause with a variable, how do I get the SELECT statement to see all of the values? Ex. since my variables contain character datatypes I obviously need to use single quotes, just as SQLSister stated, but when I pad each of the values with the single quotes it still doesn't work...any suggestions??
 
Passing a list of values to a Stored Procedure faq183-3979
Passing a list of values to a Stored Procedure (Part II) faq183-5207

See if these FAQs don;t answer alot of your questions.


Questions about posting. See faq183-874
 
Wooohoooo!!!!!!!! You are the freakin man!!!! Thanks you so much, your function worked like a charm....Don't go too far though I may need to enlist your help down the road again....You have to be a millionaire
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top