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
 SQL Server 2000 Forums
 Transact-SQL (2000)
 Finding Unused Checks

Author  Topic 

TJ
Posting Yak Master

201 Posts

Posted - 2003-04-21 : 12:03:45
I have two tables. The first table contains the check range issued to the user. The second is the Check Register Table that contains info on which checks the user has printed or voided.
ToMailBox 		StartCheck		EndCheck
(Varchar (7)) (int) (int)
-------------- ------------ ------------
01390 205901 205950

chkprtbox 		chknumber 
(varchar(5)) (varchar(7))
--------- ---------
01390 0205906
01390 0205924
01390 0205926

I need to return a recordset to a Crystal Report that lists all of the checks not used.

I have the following code that works.
declare @ID as int, declare @BegChk as int, @EndChk as int, @NoChks as int, @MBox as varchar(5), @CI as int, @ChkNo  as int,
@Out as varchar(7), @No as varchar(7), @lNo as int, @Dif as int

set @ID = 0
set @begchk = 0
set @endchk = 0
set @NoChks = 0
set @mbox = null
set @CI = 0
set @chkNo = 0
set @Out = Null
set @No = Null
set @lNo = 0
set @dif = 0

drop table tmpchkrng

create table tmpChkRng (ChkNo varchar(7), CI varchar(1))

--while (select top 1 chkrangeid from chkrange where chkrangeid > @ID) > 0
--begin
set @id = (select top 1 chkrangeid from chkrange where tomailbox = '01390') --where chkrangeid > @ID)
set @begchk = (select startcheck from chkrange where chkrangeid = @ID)
set @endchk = (select endcheck from chkrange where chkrangeid = @ID)
set @nochks = (@endchk - @begchk) + 1
set @mbox = (select tomailbox from chkrange where chkrangeid = @ID)
set @CI = (select count(chknumber)
from chkreg
where (chkprtbox = '01390' and
chknumber is not null and
chktrncode = 'ci')) --@mbox)
if @CI < @nochks
begin
set @chkno = @begchk
while @chkno < = @endchk
begin
set @No = ltrim(rtrim(cast(@chkno as varchar(7))))
set @lno = len(@no)
if @lno <> 7
begin
set @Dif = 7 - @lno
if @Dif = 1 set @No = '0' + @No
if @dif = 2 set @No = '00' + @No
if @Dif = 3 set @No = '000' + @No
end
set @Out = (select chknumber from chkreg where chknumber = @No and chkprtbox = '01390')
if @Out is null or ltrim(rtrim(@out)) = ''
begin
--print 'chkno ' + cast(@chkno as varchar(7)) + ' is missing'
insert tmpChkRng values( @chkno, 'N')
end
set @chkno = @chkno + 1
end
end
--end

I've commented out the first while loop to pare it down to one printbox for testing purposes. There are upwards of 1000+ users with the possibility of 50+ unused checks for each user.

It's very cumbersome with the while loops. Is there a way to code this without them? Is there a way to code this without the use of the temp table?

Any advice would be greatly appreciated!

Thanks!
Teresa



Edited by - TJ on 04/21/2003 12:06:05

dsdeming

479 Posts

Posted - 2003-04-21 : 12:31:56
Try using a number table. I have a table called Sequence that contains a single column -- Counter --
and 8000 rows where Counter = 1 to 8000.

create table CheckRegister
(
chkprtbox varchar(5),
chknumber varchar(7)
)


INSERT INTO CheckRegister SELECT '01390', '0205906'
INSERT INTO CheckRegister SELECT '01390', '0205924'
INSERT INTO CheckRegister SELECT '01390', '0205926'

SELECT Counter + 205901 - 1
FROM Sequence
WHERE Counter + 205901 - 1 <= 205950
AND Counter + 205901 - 1 NOT IN( SELECT chknumber FROM CheckRegister )

drop table CheckRegister


HTH



Edited by - dsdeming on 04/21/2003 13:03:07
Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2003-04-21 : 12:49:54
Don't know if there's a cleaner way than this, but....

Good Luck


CREATE TABLE myTable1 (ToMailBox varchar(7), StartCheck int, EndCheck int)
GO
CREATE TABLE myTable2 (ChkPrtBox varchar(5), ChkNumber varchar(7))
GO

INSERT INTO myTable1 (ToMailBox, StartCheck, EndCheck) SELECT '01390',205901,205950
GO
INSERT INTO myTable2 (ChkPrtBox, ChkNumber)
SELECT '01390','0205906' UNION ALL
SELECT '01390','0205924' UNION ALL
SELECT '01390','0205926'
GO

CREATE PROC myPROC @ToMailBox varchar(7) AS

SET NOCOUNT ON

DECLARE @StartCheck int, @EndCheck As int
DECLARE @mytable3 table (ChkNumber varchar(7) NOT NULL)

SELECT @StartCheck = StartCheck, @EndCheck = EndCheck FROM myTable1

WHILE @StartCheck <= @EndCheck
BEGIN
INSERT INTO @myTable3 (ChkNumber) SELECT RIGHT(REPLICATE('0',7)+Convert(varchar(7),@StartCheck),7)
SELECT @StartCheck = @StartCheck + 1
END

SELECT * FROM @myTable3 o WHERE NOT EXISTS (SELECT 1 FROM myTable2 i WHERE i.ChkNumber = o.ChkNumber)

SET NOCOUNT OFF

GO

EXEC myPROC '01390'
GO

DROP TABLE myTable1
GO
DROP TABLE myTable2
GO
DROP PROC myPROC
GO


Brett

8-)

Edited by - x002548 on 04/21/2003 12:51:32
Go to Top of Page

TJ
Posting Yak Master

201 Posts

Posted - 2003-04-21 : 13:01:27
Thanks for the input.

I'm really need to get away from the temp tables. It's possible that more than one person could fire up this report. The first person runs the sproc at 9:05 to retrieve data and the second one runs it 9:06. This is going to mess up the first person's dataset.

I have this issue with another report that I've created. I'm just not sure what the answer is. I need the recordset returned directly to the report instead of a temp table.

If you have any other solutions, or know where I might obtain additional information, please let me know.

Thanks for taking the time to respond!

Teresa

Go to Top of Page

MichaelP
Jedi Yak

2489 Posts

Posted - 2003-04-21 : 13:04:41
Correct me if I'm wrong, but aren't temp tables unique to a connection? If two users are executing a query that uses temp tables, they should not step on each other. A temp table gets a "unique" name in TempDB when you create it.

Give the temp table solution a try. I bet it will work just fine for ya.

Global Temp tables are shared across all connections. That might be what you are thinking

# = Temp table
## = Global Temp Table

Michael

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

TJ
Posting Yak Master

201 Posts

Posted - 2003-04-21 : 13:09:08
quote:
Give the temp table solution a try. I bet it will work just fine for ya.


My mistake... we create small tables and use them as temp tables. I'll check out a TRUE temp table (#) and see how that works!

Thanks!
Teresa

Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2003-04-21 : 13:18:10
Yes a LOCAL temp table is only valid for the time of the connection. A GLOBAL temp table will hang around as long as it's being referenced.

If you're using SQL2000, I would recommend using Table Datatypes as I have in my post (MOO). If you noticed there are no temp tables in it.



Brett

8-)
Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2003-04-21 : 13:21:13
quote:

I have this issue with another report that I've created. I'm just not sure what the answer is. I need the recordset returned directly to the report instead of a temp table.



I would think you're confusing a "Work" Table (an actual physically defined object) with a temp table.

In either event, I still say DECLARE a Table datatype local variable.



Brett

8-)
Go to Top of Page

TJ
Posting Yak Master

201 Posts

Posted - 2003-04-21 : 13:22:48
quote:
If you're using SQL2000, I would recommend using Table Datatypes as I have in my post (MOO). If you noticed there are no temp tables in it.


I'm sorry... I should have posted that I'm using SQL 7.

Thanks!
Teresa

Go to Top of Page

TJ
Posting Yak Master

201 Posts

Posted - 2003-04-21 : 13:24:50
quote:

I would think you're confusing a "Work" Table (an actual physically defined object) with a temp table.


You're right! I am!

Teresa

Go to Top of Page

dsdeming

479 Posts

Posted - 2003-04-21 : 13:27:47
I'm a true believer in number tables, thanks to Umachandar. They can be used to do things that are otherwise extremely awkward. Using one in this case avoids any necessity for temp tables. The number table doesn't even have to be in the same database.

DECLARE @min int, @max int

SET @min = 205900
SET @max = 205950

SELECT Counter + @min
FROM Utility..Sequence
WHERE Counter + @min <= @max
AND Counter + @min NOT IN( SELECT chknumber FROM CheckRegister )


Go to Top of Page

TJ
Posting Yak Master

201 Posts

Posted - 2003-04-21 : 13:34:50
quote:
I'm a true believer in number tables


Why do you need a number table? Why can't you use a variable and increment it? I'm trying to understand the process, but am having trouble with it since there isn't a 'work' or '#' table.

Teresa

Go to Top of Page

dsdeming

479 Posts

Posted - 2003-04-21 : 13:48:49
The number table eliminates the need for incrementing variables, using cursors, or using temp tables. A single select returns the data you're looking for.

Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2003-04-21 : 13:53:17
But don't you still have the datatype conversion problem?

And how do create and populate your sequence table.

In TJ's example she has a known range. What are the ranges in your sequence table?

EDIT: TJ since you're using 7.0, you change the datatype table to a #temp table in the code I posted, and you shouldn't have any problems with your users.

Brett

8-)

Edited by - x002548 on 04/21/2003 13:54:26
Go to Top of Page

dsdeming

479 Posts

Posted - 2003-04-21 : 14:01:18
quote:

But don't you still have the datatype conversion problem?



The code I originally posted worked fine on my system just doing an implicit conversion. Not the best practice, but this was just intended as a starting point.

quote:

And how do create and populate your sequence table.



I used a while loop to slam 8000 integers into the table in my utility database. I've never had to touch it since.

quote:

In TJ's example she has a known range. What are the ranges in your sequence table?



Her start and end numbers are the @min and @max I use to add to the Counter column of the sequence table. The sequence can always bee offset by whatever minimum value you need. My table will allow for 8000 values between @min and @max, but you could always add more.


Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2003-04-21 : 15:34:36
Looks like her range could be in the millions though

quote:

chkprtbox chknumber
(varchar(5)) (varchar(7))
--------- ---------
01390 0205906
01390 0205924
01390 0205926





Brett

8-)
Go to Top of Page

robvolk
Most Valuable Yak

15732 Posts

Posted - 2003-04-21 : 21:55:39
quote:
Looks like her range could be in the millions though
Which is a perfect lead-in to one extremely good reason NOT to use table variables, even if she had the option: you cannot index or create statistics on a table variable, and yes, you can see significantly better performance with an indexed temp table than a table variable:

http://www.sqlteam.com/Forums/topic.asp?TOPIC_ID=22212

I've also seen this effect on much smaller rowsets (2,000 - 4,000 rows) Indexing and statistics can make a very noticeable improvement in performance over table variables. And as GreatInca's post points out, table variables are almost useless in JOINs, especially to tables on disk.

Remember that even though a table variable is entirely in RAM, it is essentially a heap of data. To find anything, you have to search ALL of it. With an index, the values you are searching for will fall within hard boundaries that negate the need to search all of the available data. Also, once the temp table and its index(es) are read into memory, they stay in the data cache and are, in effect, already table variables.

And if you are going to have millions of rows of data, table variables are definitely a bad way to go. Even if you have gobs of RAM, if they overflow the available space then SQL Server will have to page the data out to the swap file, which is an expensive I/O operation compared to simply flushing some buffers and reading fresh, indexed, organized data sequentially from a disk on which it already exists (yes, even millions of rows of it...swap file I/O is completely unorganized) This will become especially noticeable if many users are running the same procedure at one time.

Go to Top of Page

byrmol
Shed Building SQL Farmer

1591 Posts

Posted - 2003-04-21 : 22:33:12
You don't need the entire numeric range. You just need to find the largest range of checks.. In this case it is 50...

A Numbers table is definately the way to go....

Using Bretts DDL and DML and a Numbers table with a column called Number

SELECT CAST(StartCheck AS INT) + Number - 1 as UnUsedChecks
FROM myTable1 CROSS JOIN Numbers
WHERE Number <= CAST(EndCheck AS INT) - CAST(StartCheck as INT) + 1
AND NOT EXISTS
(SELECT 1 FROM myTable2
WHERE ChkNumber = CAST(StartCheck AS INT) + Number - 1)


You will have to Cast/format the Check number back...

DavidM

"SQL-3 is an abomination.."

Edited by - byrmol on 04/21/2003 22:34:32
Go to Top of Page

TJ
Posting Yak Master

201 Posts

Posted - 2003-04-22 : 16:20:41
I agreed with Rob, 'cause he knows a lot about SQL , until I worked with this today.

SELECT ((tbl1.StartCheck +Number) - 1) as ChkNo,
tomailbox as mBox,
tbl1.startcheck as Start#,
tbl1.endcheck as End#,
frmmailbox as BnkMailbox
FROM tbl1
CROSS JOIN Numbers
WHERE Number <= CAST(((tbl1.EndCheck - tbl1.StartCheck )+1)as varchar(7))
AND NOT EXISTS (SELECT 1
FROM tbl2
WHERE Chknumber = replicate ('0', 7 - len(ltrim(rtrim(cast(tbl1.startcheck +number - 1 as varchar)))))
+ltrim(rtrim(cast(tbl1.startcheck +number - 1 as varchar)))
)


This alleviated the need for any kind of work or # table and the sproc runs in 14 seconds compared to 1+ minute. The work & # tables were indexed and I included an order by and it still didn't perform as well as this code. My co-worker had used replicate in one of his sprocs and helped me with the data conversion.

Thanks to all who helped. While I was skeptical at first, I've become a believer too.

Best regards from a grateful recipient of the help from the wonderful, outstanding, stupendous, SQL TEAM!!!!

Teresa


Go to Top of Page

jsmith8858
Dr. Cross Join

7423 Posts

Posted - 2003-04-22 : 16:53:15
Nice one, Byrmol !!


- Jeff
Go to Top of Page

robvolk
Most Valuable Yak

15732 Posts

Posted - 2003-04-22 : 21:12:23
quote:
I agreed with Rob, 'cause he knows a lot about SQL , until I worked with this today.
HA! That'll be the last time you ever agree with me!

I should've clarified that you didn't need a TEMP table either...just any regular, indexable table would work, especially a numbers table like everyone suggested.

And I'm also a HUUUUUUUUUUUGE believer in numbers/sequence/tally tables:

http://www.sqlteam.com/item.asp?ItemID=2652

Go to Top of Page
    Next Page

- Advertisement -