Hi,
I am new to writing Stored Procedures, having never used them before. I have extensively been writing all my SQL code using parameterized queries, but have since been repentant having seen the error of my ways, and wish to convert.
And I need some help, because I do not know how to write complex stored procedures. I have searched online, and know the syntax for a basic stored proc.
To begin my baptization with a simple project, I'm working on migrating my LAMP website to Windows, IIS, SQL Server, and ASP.Net (using Visual Basic.Net).
I'm posting a code snipped below of a sample code from my php-MySql website that I'm trying to convert to a SQL Server stored proc. (Please pardon the code that seems to be all over the place without any logical n-tier segregation. That is what I aim to fix
by switching platforms.)
Basically what this code does is to show a list of all photo albums, and then shows the number of photos in each album. It also displays the number of total albums retrieved on top of the page.
There are three tables -
Albums
Pictures
AlbumPictures
The first two tables are self-explanatory, and the third table establishes a many-to-many relationship between Pictures and Albums, and has two fields - the primary keys of both the first two tables. Meaning I can have one picture show up in multiple albums.
-------php code follows------
/* -- counts all records in albums table, and displays the total as the number of albums on the top of the webpage -- */ $query3 = "Select Count(*) From Albums"; $result3 = mysqli_query($con, $query3) or die ("error message"); $row3 = mysqli_fetch_row($result3); echo "<span style='font-size:14pt; color: #000000'><b>Albums</b> ($row3[0])</span>\n"; /* -- retrieves data of all albums present in the albums table, and formats and displays them on the webpage in an html table. -- */ $query1 = "Select * From Albums"; $result1 = mysqli_query($con, $query1) or die ("error message"); while($row1 = mysqli_fetch_assoc($result1)) { extract($row1); /* -- if no albums cover has been specified, then display a default album cover image. -- */ if($AlbumCover == "") { $AlbumCover = "http://www.mywebsite.com/images/no_album_cover.gif"; } /* -- for each individual photo album entry in the Albums table, count the number of records in the albumspictures table for that particular album, and display number of photos for that album alongside data for that album on the webpage. -- */ $query2 = "Select COUNT(*) From AlbumPictures Where AlbumNo = '$AlbumNo'"; /* -- The variable $AlbumNo passed in the where clause in the sql statement above is the value of the AlbumNo column for the current record from the sql statement executed in $query1. This is a php thing, all column names from a select statement are returned as variables with the same name as the column. --*/ $result2 = mysqli_query($con, $query2) or die ("error message."); $row2 = mysqli_fetch_row($result2); if($row2[0] == 0) { $MediaPicture = "\"javascript:void(0)\" onclick=\"alert('No pictures have been added to this album yet.')\""; $Alttext = "No Pictures in this album"; $Phototext = "Photos"; } elseif($row2[0] == 1) { $MediaPicture = "\"javascript:void(0)\" onclick=\"window.open('http://www.mywebsite.com/pictures.php?id=$AlbumNo')\""; $Alttext = "1 Picture in this album"; $Phototext = "Photo"; } else { $MediaPicture = "\"javascript:void(0)\" onclick=\"window.open('http://www.mywebsite.com/pictures.php?id=$AlbumNo')\""; $Alttext = "$row2[0] Pictures in this album"; $Phototext = "Photos"; } echo "<br /><table>\n<col width=370>\n<col width=500 valign=top>\n<tr>\n<td>\n<a href=$MediaPicture><img src='$AlbumCover' alt='$Alttext' width='320' border=0></a></td>\n<td>\n<table>\n<tr>\n<td>\n<span style='color: #000000'><b>$AlbumName</b> ($row2[0] $Phototext)</span></td>\n</tr>\n<tr>\n<td>\n<span style='color: #000000'>$AlbumDesc</span></td>\n</tr>\n<tr>\n<td>\n<a href=$MediaPicture>View Album</a></td>\n</tr>\n</table>\n</td>\n</tr>\n</table>\n"; }
So this means I need to write a stored procedure with SQL code that does the following -
Selects All from Albums table. All Select statements also return a rowcount of how many rows were affected (unless NOCOUNT is set to ON), and instead of running a second sql query (like $query3 from the sample above), I could simply use the rowsaffectedcount returned by SQL Server, but do not know how.
For each album record retrieved from the albums table, I need to run a second query on another table that returns the number of pictures in this album. (I understand I am taking a cursor-based approach in the MySql example above, and that SQL Server works better with a set-based approach, but again, I do not know the best way to accomplish this, and would appreciate some help.)
I could come up with this much -
Create Procedure myschema.retrievealbumdata
As
Begin
Select * From Albums
/* Now how do I retrieve the value of number of rows affected and display it without having to run a COUNT(*) query? Also, since the primary job this stored proc is doing is running a Select, which I will be reading using a DataReader in my VB.Net code, won't this number-of-rows-affected value affect my DataReader? Do I need to use an output parameter in this case? If so, then how? */
End
Go
Create Procedure myschema.countpicturesinalbum
@AlbumNo int
As
Begin
Select COUNT(*) From AlbumPictures Where AlbumNo = @AlbumNo
EndGo
In VB.Net -
Sub MySub() Dim cmd, cmd2 as SqlCommand Dim con as new SqlConnection("My connection string") Dim dt, dt2 as new DataTable() cmd = new SqlCommand cmd.CommandText = "retrievealbumdata" cmd.CommandType = CommandType.StoredProcedure cmd.Connection = con dt.clear() con.open() Dim dr as SqlDataReader = cmd.ExecuteReader() dt.Load(dr) dr.Close() con.close() For i As Integer = 0 To dt.Rows.Count - 1 cmd2 = new SqlCommand cmd2.CommandText = "countpicturesinalbum" cmd2.CommandType = CommandType.StoredProcedure cmd2.Parameters.AddWithValue("@AlbumNo", CInt(dt.Rows(i).Item(0))) cmd2.Connection = con dt2.clear() con.open() Dim dr2 as SqlDataReader = cmd2.ExecuteReader() dt2.Load(dr2) dr2.Close() con.Close() Next End Sub
(The code above was hastily put together. I would actually have loaded all the data in the same datatable, and would have written additional code for it. But I'm just using two separate datatables here for the same thing for the sake of simplicity.)
Now that's a lot of database calls!
Is there a more efficient way of doing this? Can/Should I just call the second stored proc from within the first one? Would it end up using the slow, Cursor based approach?
How do I do this?
(I tend not to use the datasets, dataadapters and other binding objects and in-built ADO.NET classes exposed by Visual Studio, and generally write my own hand-rolled code for all database access. I prefer working with datatables for all sets of records returned
by Select statements. Therefore I would appreciate examples using datatables, and not the built-in binding ADO.Net objects.)
Lastly, I tried searching a lot online, bu couldn't find anything pertinent.
Is there a good resource or a book where I can learn more about the powerful functionality of stored procedures? Something written in a simple language with beginners in mind?