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
 SQL Server Development (2000)
 Do I need cursors or not?

Author  Topic 

label
Posting Yak Master

197 Posts

Posted - 2004-03-08 : 10:38:35
Here's the problem:

1. Someone gives me a partnumber. That partnumber could look like this: "MGPM-32-SW92-X90"
2. I need to return a patternCode that corresponds with that Partnumber. That patternCode may look like this "2139"
3. I have a search_table that narrows down which patternCode it might be from 2000 to about 2-7 possible pattern codes. Once I have those Possible matches, I go to step 4.
4. In the database, I have a couple of tables that are relevant.

Table 1:
sdb_pattern_block

Revelant Columns
Pattern_Code - It's what I'm looking for.
Block_ID - for each block_index that's a part of a pattern_code, there's a unique block_id assigned to it.
Block_Index - the order in which each block_id comes in the string

In this table, some relevant results for a single pattern code might be:


Pattern BlockId Blockindex
2139 11253 0
2139 11259 1
2139 11258 2
2139 11252 3
2139 11257 4
2139 11256 5
2139 11255 6
2139 11254 7


Table 2:
sdb_block_value

relevant columns:
Block_Id (links to sdb_pattern_block.block_id)
Value_Code (is part of the part number string
Value_Index (is the order in which the value comes inside it's block_index)

Some relevant data with the block_index thrown in from the sdb_pattern_block table for a single pattern Code would like this this.


BlockID Blockindex ValueCode Valueindex
11253 0 MGPM 0
11259 1 12 0
11259 1 16 1
11259 1 20 2
11259 1 25 3
11259 1 32 4
11259 1 40 5
11259 1 50 6
11259 1 63 7
11259 1 80 8
11259 1 100 9
11258 2 0
11258 2 N 1
11258 2 TF 2
11252 3 - 0
11257 4 10 0
11257 4 20 1
11257 4 25 2
11257 4 30 3
11257 4 40 4
11257 4 50 5
11257 4 75 6
11257 4 100 7
11257 4 125 8
11257 4 150 9
11257 4 175 10
11257 4 200 11
11256 5 0
11256 5 - 1
11255 6 0
11255 6 Y59A 1
11255 6 Y59AL 2
11255 6 Y59AZ 3
11255 6 Y59B 4
11255 6 Y59BL 5
11255 6 Y59BZ 6
11255 6 Y69A 7
11255 6 Y69AL 8
11255 6 Y69AZ 9
11255 6 Y69B 10
11255 6 Y69BL 11
11255 6 Y69BZ 12
11255 6 Y7BAL 13
11255 6 Y7BAZ 14
11255 6 Y7BW 15
11255 6 Y7BWL 16
11255 6 Y7BWV 17
11255 6 Y7BWVL 18
11255 6 Y7BWVZ 19
11255 6 Y7BWZ 20
11255 6 Y7NW 21
11255 6 Y7NWL 22
11255 6 Y7NWV 23
11255 6 Y7NWVL 24
11255 6 Y7NWVZ 25
11255 6 Y7NWZ 26
11255 6 Y7P 27
11255 6 Y7PL 28
11255 6 Y7PV 29
11255 6 Y7PVL 30
11255 6 Y7PVZ 31
11255 6 Y7PW 32
11255 6 Y7PWL 33
11255 6 Y7PWV 34
11255 6 Y7PWVL 35
11255 6 Y7PWVZ 36
11255 6 Y7PWZ 37
11255 6 Y7PZ 38
11255 6 Z73 39
11255 6 Z73L 40
11255 6 Z73Z 41
11255 6 Z76 42
11255 6 Z76L 43
11255 6 Z80 44
11255 6 Z80L 45
11254 7 3 0
11254 7 4 1
11254 7 2
11254 7 S 3


As you can see there are multiple possible values for most block_ids/indexes.

The sql for that is:


SELECT a.block_id, block_index, value_code, value_index
FROM sdb_block_value a
join
sdb_pattern_block b
on
a.block_id=b.block_id
and
pattern_code in ('2139')
order by block_index, value_index


Now, I have no idea when someone enters a partnumber which part of the string corresponds with which index in a given pattern code. So, the only way I can think to solve this problem is this:

1. Grab all data for possible pattern code matches.
2. Compare all of the values in the first block_index from the first pattern_code and see if any of the values matches with the partnumber. Psuedo code would be:


select @curPartNumber=@curPartNumber + value_code
from #temp
where pattern_code=@curpattern
and block_index=@curIndex
and value_index=@curVindex

if @curPartNumber=substring(@partnumber, 0, len(@curPartNumber))
begin
set @isMatch_Value=1
end


3. If that matches, move up to the next block_index in the current patternCode and add to the @curPartNumber string the possible values in that index. If any of them match, continue. If none match, move to the next pattern code and start over.

4. Eventually, by adding one value at a time I should find a part number where all of the possible values will be in order and correspond with one of the patterncodes data I've got returned.

If there's any way to perform this type of a search without Cursors, please let me know.

My current SQL looks like this:


CREATE PROCEDURE dbo.ap_get_pattern_from_partnumber

@partnumber varchar(50)

AS

Begin

set nocount on

create table #temp (pattern_code varchar(6), block_index int, value_code varchar(20), value_index int)
create table #temp2 (pattern_code varchar(6))

insert into #temp
SELECT pattern_code, block_index, value_code, value_index
FROM sdb_block_value a
join
sdb_pattern_block b
on
a.block_id=b.block_id
and
pattern_code in ('2139', '2170', '2172', '2186')
order by block_index, value_index

--Grab distinct pattern codes
Declare cursID cursor for
select distinct(pattern_code) from #temp

Declare @curPattern varchar(6),
@isMatch_Value bit,
@isMatch_Block bit,
@isMatch_Pattern bit,
@curPartNumber varchar(50)

set @isMatch_Value=0
set @isMatch_Block=0
set @isMatch_Pattern=0

--Begin First Loop
open cursID
fetch next from cursid into @curPattern
while (@@fetch_status<>-1)
Begin

Declare Cursid2 cursor for
select block_index from #temp
where pattern_code=@curPattern

Declare @curIndex int,
@curmatch varchar,
@minlength int

set @curmatch=''
set @minlength=0

--Begin Second Loop
open cursid2
fetch next from cursid2 into @curIndex
while (@@fetch_status<>-1)
Begin

Declare cursid3 cursor for
select value_index from #temp
where pattern_code=@curPattern
and block_index=@curIndex

Declare @curvIndex int

--Begin Third loop
Open cursid3
fetch next from cursid3 into @curVindex
while (@@fetch_status<>-1)
Begin
select @minlength=len(value_code)
from #temp
where pattern_code=@curPattern
and block_index=@curIndex
and value_index

select @curPartNumber=@curPartNumber + value_code
from #temp
where pattern_code=@curpattern
and block_index=@curIndex
and value_index=@curVindex

if @curPartNumber=substring(@partnumber, 0, len(@curPartNumber))
begin
set @isMatch_Value=1
end

if @isMatch_value=1
Begin
set @isMatch_value=0
fetch next from cursid3 into @curVindex
set @isMatch_Block=1
end
else
begin
set @isMatch_value=0
set @isMatch_Block=0
set @curPartNumber=''
end

end
deallocate cursid3

if @isMatch_block=1
Begin
set @isMatch_Pattern=1
fetch next from cursid2 into @curIndex
end
else
Begin
set @isMatch_pattern=0
set @isMatch_block=0
end

end
deallocate cursid2

if @isMatch_Pattern=1
begin
insert into #temp2
select @curPattern
end

fetch next from cursid into @curPattern
end
deallocate cursid



End

nr
SQLTeam MVY

12543 Posts

Posted - 2004-03-08 : 11:06:37
>> Do I need cursors or not?
No.
(Without even reading the reast of the question).

==========================================
Cursors are useful if you don't know sql.
DTS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

ehorn
Master Smack Fu Yak Hacker

1632 Posts

Posted - 2004-03-08 : 11:08:51
[code]
--Create Tally Table
create table numbers ( n int)
declare @n int; set @n = 0
while @n <=1000
begin
insert into numbers
select @n
set @n = @n + 1
end
GO

declare @part_no varchar(30)
set @part_no = 'MGPM-32-SW92-X90'

select *
from
(
--parse the partnumber
select NullIf(SubString('-' + @part_no + '-' , n , CharIndex('-' , '-' + @part_no + '-' , n) - n) , '') AS block
from numbers
where n <= Len('-' + @part_no + '-') AND SubString('-' + @part_no + '-' , n - 1, 1) = '-'
) d
join sdb_block_value bv on bv.value_code = d.block
join sdb_pattern_block pb on pb.block_id = bv.block_id and pb.pattern_code in ('2139')
[/code]
Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2004-03-08 : 11:27:20
quote:
Originally posted by nr

>> Do I need cursors or not?
No.
(Without even reading the reast of the question).

==========================================
Cursors are useful if you don't know sql.
DTS can be used in a similar way.
Beer is not cold and it isn't fizzy.



That's because Nigel is clairvoyant


Who is claire by the way?



Brett

8-)
Go to Top of Page

label
Posting Yak Master

197 Posts

Posted - 2004-03-08 : 11:38:06
quote:
Originally posted by nr

>> Do I need cursors or not?
No.
(Without even reading the reast of the question).




While that may or may not be true, your response in no way, shape or form helps me.
Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2004-03-08 : 11:42:15
quote:
Originally posted by label

quote:
Originally posted by nr

>> Do I need cursors or not?
No.
(Without even reading the reast of the question).




While that may or may not be true, your response in no way, shape or form helps me.



So take it or leave it...Didn't Jay's response give you some insight, or not?



Brett

8-)
Go to Top of Page

label
Posting Yak Master

197 Posts

Posted - 2004-03-08 : 12:05:08
quote:
Originally posted by ehorn


--Create Tally Table




Ok, I created the Numbers table and ran the following sql


select *
from
(
--parse the partnumber
select NullIf(SubString('-' + @part_no + '-' , n , CharIndex('-' , '-' + @part_no + '-' , n) - n) , '') AS block
from numbers
where n <= Len('-' + @part_no + '-') AND SubString('-' + @part_no + '-' , n - 1, 1) = '-'
) d
join sdb_block_value bv on bv.value_code = d.block
join sdb_pattern_block pb on pb.block_id = bv.block_id and pb.pattern_code in ('2139', '2186', '2170', '2172')


The results I got were these:

[code]
200 8991 7 200 NULL 200mm 2186 8991 Stroke NULL 4 1 -1
Y59AZ 8994 5 Y59AZ NULL Y59AZ--Solid State, Gen. Purpose, 3 Wire NPN, Horizontal, 5M 2186 8994 Auto Switch NULL 9 1 0
200 11257 11 200 NULL 200mm 2139 11257 Stroke NULL 4 1 -1
Y59AZ 11255 3 Y59AZ NULL Y59AZ--Solid State, Gen. Purpose, 3 Wire NPN, Horizontal, 5M 2139 11255 Auto Switch NULL 6 1 0
200 11361 3 200 NULL 200mm 2170 11361 Stroke NULL 5 1 -1
Y59AZ 11353 3 Y59AZ NULL Y59AZ--Solid State, Gen. Purpose, 3 Wire NPN, Horizontal, 5M 2170 11353 Auto Switch NULL 7 1 0
200 11367 7 200 NULL 200mm 2172 11367 Stroke NULL 5 1 -1
Y59AZ 11364 3 Y59AZ NULL Y59AZ--Solid State, Gen. Purpose, 3 Wire NPN, Horizontal, 5M 2172 11364 Auto Switch NULL 8 1 0



What are these results telling me? I'm still not sure which pattern code is the match out of the four I started with?
Go to Top of Page

label
Posting Yak Master

197 Posts

Posted - 2004-03-08 : 12:09:01
quote:
Originally posted by X002548
So take it or leave it...Didn't Jay's response give you some insight, or not?

Brett

8-)



A) There's nothing to "take" or "leave" from that statement as to this point I've simply been told I don't need cursors but have been given no explanation or examples of why I don't and how to solve the problem I've described. If I really can do this without Cursors, that's wonderful, but I'm totally unaware of how to go about returning the data I want any other way.

B) I've plugged Jay's code in but the results I got back didn't seem to clear up the problem. Either I don't understand the results or there's been a breakdown in communication somewhere in me describing what I want back.
Go to Top of Page

ChrisFretwell
Starting Member

43 Posts

Posted - 2004-03-08 : 12:44:31
For A: Cursors are considered 'bad' in sql. In almost every situation, a non-cursor solution is available and faster. That being said, if you've ever read any of the system sps created by MS, many use cursors (go ahead look at things like sp_helpdb, sp_helpindex - although in 2000 many of the older cursor based sps now use work and temp tables and fewer cursors). The few times cursors are the most useful are when you need to look at data in order and the behaviour of a specific record depends on the values of the previous or next record. If the code is wrapped in an application, the application usually takes care of this part.
To save time, can you post ddl and dml code to create a sample set of data for both your tables, an input statement and the expected return results. It will save all of us time.

chris

Back on topic, you should be able to replace your cursor with a series of statements. Exactly which ones, I need more time for to look at what you want. Meantime, and so as not to repeat returning exactly what Jay returned using different code, what was returned from Jay's code that you didnt want returned? Can you take Jay's code and figure out where its not exactly what you want and modify to do what you want?
Go to Top of Page

nr
SQLTeam MVY

12543 Posts

Posted - 2004-03-08 : 12:59:33
quote:
Originally posted by label

quote:
Originally posted by nr

>> Do I need cursors or not?
No.
(Without even reading the reast of the question).




While that may or may not be true, your response in no way, shape or form helps me.



Sorry - to be so terse with my response - I meant to look at the problem a follow it with a longer response but got distracted.
Also didn't realise this was a continuation of the other thread where I'd already made the pertinent comment.

My fault - I now have to look though the thread to see if what I was going to post is valid or already said.


==========================================
Cursors are useful if you don't know sql.
DTS can be used in a similar way.
Beer is not cold and it isn't fizzy.
Go to Top of Page

label
Posting Yak Master

197 Posts

Posted - 2004-03-08 : 13:04:04
quote:
Originally posted by ChrisFretwell

For A: Cursors are considered 'bad' in sql. In almost every situation, a non-cursor solution is available and faster.


Right, I do understand cursors are less than desirable and I try to avoid them whenever possible. (Much like Frames in Html/.NET ) However, my skills being somewhat limited in SQL there are times when I turn to cursors because I know no other way of solving the problem.

quote:
To save time, can you post ddl and dml code to create a sample set of data for both your tables, an input statement and the expected return results. It will save all of us time.


I can do better than that. I've created a new database, imported the two relevant tables, zipped them up and they are less than 2 megs. I'll be happy to email them to anyone wanting to attach the database and test it out.

quote:
Back on topic, you should be able to replace your cursor with a series of statements. Exactly which ones, I need more time for to look at what you want. Meantime, and so as not to repeat returning exactly what Jay returned using different code, what was returned from Jay's code that you didnt want returned?


I'm just not sure what to do with the data it spit out. I want returned a sinlge Pattern Code that my partnumber belongs to.

If someone types in "MGPM80-200-Y59AZ" (which is the test partnumber I'm actually using here) I want the results to be "2139".
Go to Top of Page

label
Posting Yak Master

197 Posts

Posted - 2004-03-08 : 13:08:18
quote:
Originally posted by nr
Sorry - to be so terse with my response - I meant to look at the problem a follow it with a longer response but got distracted.
Also didn't realise this was a continuation of the other thread where I'd already made the pertinent comment.

My fault - I now have to look though the thread to see if what I was going to post is valid or already said.



No problem at all. I'm genuinely grateful for any help anyone here can give. As you can tell, SQL is obviously not my strong suit.
Go to Top of Page

ehorn
Master Smack Fu Yak Hacker

1632 Posts

Posted - 2004-03-08 : 16:08:53
label,

Sorry for the long delay in getting back to this. I have a few questions if you are still working to resolve this without using cursors:

Given the following example: Part number = MGPM-80-200-Y59AZ

1.) Is a part number "block" always seperated by a "-" ? ie this part number contains blocks 0-3

2.) Does sdb_pattern_block.Block_Index correlate to the position of the block in the part number? ie.match value code of "MGPN" with a value code were block_index = 0 then move onto "80" with block_index=1, etc.. until we find a pattern_code that meets all these patterns/blocks...
Go to Top of Page

label
Posting Yak Master

197 Posts

Posted - 2004-03-08 : 16:49:50
quote:
Originally posted by ehorn

label,

Sorry for the long delay in getting back to this. I have a few questions if you are still working to resolve this without using cursors:


No problem on the delay.

However, I solved the problem using .NET, where I'm a ton more comfortable. I simply put the results of each pattern code's data into it's own Datatable and stuck all those datatables into a Dataset, set up some simple logic and got it to work in about 30-40 lines or so instead of the 100+ of my stored proc.

Thanks for everyone's suggestions and advice, they are always appreciated.
Go to Top of Page

X002548
Not Just a Number

15586 Posts

Posted - 2004-03-09 : 10:43:10
I think we would all be interested in your solution....



Brett

8-)
Go to Top of Page

label
Posting Yak Master

197 Posts

Posted - 2004-03-09 : 19:46:29
quote:
Originally posted by X002548

I think we would all be interested in your solution....




No problem. I'll post it here tommorrow. I'm at home right now...

(It's written in ASP.NET 1.1)
Go to Top of Page

jsmith8858
Dr. Cross Join

7423 Posts

Posted - 2004-03-09 : 20:30:33
Label -- didn't I solve this one for you already?

http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=28879

why are you back to the cursor?

also -- why don't you just tranform your data into a "Search table" one time, leave it like that, and then just search that table for your patterns? As part of your process, when your pattern data changes, you then just rebuld it, using a technique like the one I gave you?

you shouldn't be rebulding your search data on every call ... do it only when the pattern data changes, otherwise it is a constant.

- Jeff
Go to Top of Page

label
Posting Yak Master

197 Posts

Posted - 2004-03-11 : 08:10:11
Ok, as promised here's the .NET Function that returns a patterncode from any given partnumber



Private Function get_pattern_code(ByVal partnumber As String) As String
'This section just does a very quick search that returns just the 1-8 possible canidates rather than searching through all 2000 of them
'*****************************************************************
Dim curSearch As String
Dim dashIndex As Integer = partnumber.IndexOf("-", 0)
If dashIndex = 6 Then
curSearch = partnumber.Substring(0, (partnumber.IndexOf("-", 0) + 1))
ElseIf partnumber.Length <= 6 Then
curSearch = partnumber
Else
curSearch = partnumber.Substring(0, 6)
End If
Dim DS As DataSet = eDB.get_search_tables(curSearch)
'*****************************************************************

'If only one table was returned, that's our winner
'*****************************************************************
If DS.Tables.Count = 1 Then
Return DS.Tables(0).TableName & "_" & DS.Tables(0).Rows(0).Item("model")
End If

Dim dt As DataTable
Dim dr As DataRow

Dim curvalue As String
Dim curPattern As String = ""
Dim curModel As String = ""
Dim valueLength As Integer = 0

Dim curIndex As Integer = 0
Dim nextIndex As Integer = 0
Dim blockMatch As Integer = -1

Dim isReq As Boolean = True
Dim wasReq As Boolean = True
Dim isMatch As Boolean = False
Dim goNext As Boolean = False

Dim Temp_Partnumber As String = ""
Dim Search_Partnumber As String = partnumber

'Loop through the datatables in memory and return Pattern Code
'*****************************************************************
For Each dt In DS.Tables
curPattern = dt.TableName.ToString
curModel = dt.Rows(0).Item("model")
curIndex = 0
Temp_Partnumber = ""

For Each dr In dt.Rows
nextIndex = dr.Item("block_index")
curvalue = dr.Item("value_code")
valueLength = (Temp_Partnumber.Length + curvalue.Length)
isReq = CType(dr.Item("block_isrequired"), Boolean)
isMatch = False

'Make sure the length of the partnumber we're about to check is smaller/the same length as the part we're searching for
'***************************************************************************************
If valueLength <= Search_Partnumber.Length Then

'If we are moving up a block do this
'***************************************************************************************
If (nextIndex > curIndex) And (goNext = True Or wasReq = False) Then
goNext = False
curIndex = nextIndex
wasReq = isReq

If Search_Partnumber.Substring(0, valueLength) = Temp_Partnumber & curvalue Then
Temp_Partnumber = Temp_Partnumber & curvalue
isMatch = True
End If

If isMatch = True Then
goNext = True
End If
ElseIf (nextIndex = curIndex) And goNext = False Then
If Search_Partnumber.Substring(0, valueLength) = Temp_Partnumber & curvalue Then
Temp_Partnumber = Temp_Partnumber & curvalue
isMatch = True
End If

If isMatch = True Then
goNext = True
End If
End If

'Check to see if we have a final match
'***************************************************************************************
If Search_Partnumber = Temp_Partnumber Then
Return curPattern & "_" & curModel
End If
End If
Next
Next

Return ""
End Function


The actual code that builds the string and handles all the logic once the tables are returned as you can see is only about 20-30 lines and it returns the patterncode almost instantly everytime so all in all I'm pretty pleased.
Go to Top of Page

label
Posting Yak Master

197 Posts

Posted - 2004-03-11 : 08:27:56
quote:
Originally posted by jsmith8858

Label -- didn't I solve this one for you already?

http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=28879



Looking back at that thread, the problem I had was simliar but a little different than the current one I just solved. In that thread I was looking to populate a table with all the possible partnumbers that were contained in a given patterncode (which could be anywhere from 30-300 variations).

The Union query you gave me returned these results:


2001@ - 5
2001@ - 9
2001@ - 11
2001@ 10 4
2001@ 10 6
2001@ 100 6
2001@ A 10
2001@ B 10
2001@ C 0
2001@ C 7
2001@ D 1
2002 - 5
2002 - 8
2002 - 10
2002 10 4
2002 10 6
2002 3 12
2002 6 4
2002 B 3
2002 B 9
2002 C 0
2002 D 1
2002 D 3
2002 D 7


That's not the type of results I need. I was needing results like this:

ABCDEF
ACCDEF
ADCDEF
ABDDEF
ACDDEF
ADDDEG

In other words, fully formed partnumbers representing every possible permuation of a given set of data.

Currently though I'm not trying to do that. My current problem is that I need to return a single pattern code from a partnumber (which may be partial).

It's kind of the inverse of the problem I had before. Someone feeds my a partnumber (which is what we were trying to build the last time around from a patterncode) and I return which Patterncode that partnumber belongs to keeping the following in mind.

1. The values have to be in the proper order according to the blockindexs of a pattern.
2. Some blockindexs are required, others are not.
3. In some blockindexes you may have values that might match the current block and the next block so once a block finds a match, it cannot match on another value from that block again.

The code I posted is able to navigate through those blocks and in real time determine if the current value being evaluated matches all of those criteria or not.


quote:

why are you back to the cursor?


Mostly out of ignorance of a better solution. SQL is not my first language. .NET is and so I sometimes find myself turning to less than ideal solutions in SQL when I can't figure out any other way to do it.

quote:

also -- why don't you just tranform your data into a "Search table" one time, leave it like that, and then just search that table for your patterns?


Because the resulting search table would be well over 2 billion records from all pattern codes. Besides, storing every variation of a fully formed partnumber from every pattern code would seem to be a very ineffecient method of housing the data when I can currently house all the possible permuations of all the pattern codes in a mere 13,396 rows. I see no reason to try and express every potential of those permuations when I can by my code above, analyze the values where they are and return what I need without searching through a megatable of all the possible partnumbers.

Also, these partnumbers are always changing so I'd be rerunning that query to populate the table all the time.


quote:
you shouldn't be rebulding your search data on every call ... do it only when the pattern data changes, otherwise it is a constant.


The way I'm doing it now is that I'm only getting the basic, unassembled block values for a select few pattern codes and evaluating their potentials dynamically without ever having to express every possible permutation of a partnumber.

I hope that helps clear up some of my thinking. If I'm not understanding some of your questions or points, please feel free to question or challenge my logic as I always enjoy improving my skills and learning more.

Thanks.
Go to Top of Page
   

- Advertisement -