Using a Dynamic IN Clause With Master-Detail Records - Demo
This example is based on Scott Mitchell's article at Displaying Detail Records for User-Selected Master Records :: Using a Dynamic IN Clause
I frequently have the need to pass a dynamically generated list of record IDs to a query or stored procedure either to select or delete a list of records. Unfortunately,
something as simple as this does not work in SQL Server.
SELECT MovieID
FROM Movies
WHERE MovieID IN (@ListOfMovieIDs)
One workaround, this is to use a subquery with the select statement.
SELECT MovieID
FROM Movies
WHERE MovieID IN (SELECT MovieID FROM Movies WHERE MovieName LIKE '%Horror%')
The other approach is to create a SQL Server User-Defined Function (UDF) like this:
CREATE FUNCTION dbo.udf_Split
(
@List nvarchar(2000),
@SplitOn nvarchar(5)
)
RETURNS @RtnValue table
(
Id int identity(1,1),
Value nvarchar(100)
)
AS
BEGIN
While (Charindex(@SplitOn, @List) > 0)
Begin
Insert Into @RtnValue (value)
Select Value = ltrim(rtrim(Substring(@List, 1, Charindex(@SplitOn, @List) - 1)))
Set @List = Substring(@List,Charindex(@SplitOn, @List) + len(@SplitOn), len(@List))
End
Insert Into @RtnValue (Value)
Select Value = ltrim(rtrim(@List))
Return
END
The udf_Split UDF accepts two string parameters as input - the list and a delimiter. It returns a table-valued result that contains two columns - Id, an auto-incrementing identifier, and Value. Because this UDF returns a tabular result, you can use it in the FROM clause.
To call the function, you need to do this:
SELECT Columns
FROM Movies
WHERE MovieID IN (SELECT CAST(Value AS int) FROM dbo.udf_Split(@ListOfMovieIDs, ','))
Displaying the Details Records Belonging to the Selected Master Records
When we put it all together this is the outcome
<%@ Page Language="VB" CodeFile="SplitUDF.aspx.vb" Inherits="MoviesByCategory" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Using a UDF Split Function</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<h2>Choose the Movie Categories to View</h2>
<p>
<asp:CheckBoxList ID="CategoryList" runat="server"
DataSourceID="CategoriesDataSource" DataTextField="Name"
DataValueField="ID" RepeatDirection="Horizontal" RepeatColumns="4">
</asp:CheckBoxList>
<asp:SqlDataSource ID="CategoriesDataSource" runat="server"
ConnectionString="<%$ ConnectionStrings:MyDatabase %>"
SelectCommand="SELECT [ID], [Name] FROM [MovieCategories] ORDER BY [Name]">
</asp:SqlDataSource>
<asp:Button ID="RefreshMovies" runat="server" Text="Update Movie List" />
</p>
<p>
<asp:GridView ID="MoviesBySelectedCategoriesGrid" runat="server"
CellPadding="4" DataSourceID="MoviesBySelectedCategoriesDataSource"
ForeColor="#333333" GridLines="None" AutoGenerateColumns="False"
AllowSorting="True"
Width="95%"
EmptyDataText="There are no movies that belong to the categories you have selected."
AllowPaging="True">
<FooterStyle BackColor="#1C5E55" Font-Bold="True" ForeColor="White" />
<RowStyle BackColor="#E3EAEB" />
<EmptyDataRowStyle Font-Italic="True" />
<Columns>
<asp:BoundField DataField="Title" HeaderText="Title"
SortExpression="Title" />
<asp:BoundField DataField="Name" HeaderText="Category"
SortExpression="Name" />
<asp:BoundField DataField="Director" HeaderText="Director"
SortExpression="Director" />
<asp:BoundField DataField="BoxOfficeTotals" DataFormatString="{0:c}"
HeaderText="Box Office Totals" SortExpression="BoxOfficeTotals" />
</Columns>
<PagerStyle BackColor="#666666" ForeColor="White" HorizontalAlign="Center" />
<SelectedRowStyle BackColor="#C5BBAF" Font-Bold="True" ForeColor="#333333" />
<HeaderStyle BackColor="#1C5E55" Font-Bold="True" ForeColor="White" />
<EditRowStyle BackColor="#7C6F57" />
<AlternatingRowStyle BackColor="White" />
</asp:GridView>
<asp:SqlDataSource ID="MoviesBySelectedCategoriesDataSource" runat="server"
ConnectionString="<%$ ConnectionStrings:MyDatabase %>"
SelectCommand="SELECT Movies.Title, MovieCategories.Name, Movies.Director, Movies.BoxOfficeTotals
FROM Movies INNER JOIN MovieCategories ON Movies.CategoryID = MovieCategories.ID WHERE Movies.CategoryID
IN (SELECT CAST(Value as int) FROM dbo.udf_Split(@SelectedCategoryIDs, ',')) ORDER BY Name">
<SelectParameters>
<asp:Parameter Name="SelectedCategoryIDs" />
</SelectParameters>
</asp:SqlDataSource>
</p>
</div>
</form>
</body>
</html>
** SplitUDF.aspx.vb **
Imports System.Data
Imports System.Data.SqlClient
Imports System.Collections.Generic
Partial Class MoviesByCategory
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'On the first page visit populate the user's default Category selections
End Sub
Protected Sub RefreshProducts_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles RefreshMovies.Click
'Rebind the data to the grid
MoviesBySelectedCategoriesGrid.DataBind()
End Sub
Protected Sub MoviesBySelectedCategoriesDataSource_Selecting(ByVal sender As Object,
ByVal e As System.Web.UI.WebControls.SqlDataSourceSelectingEventArgs) Handles MoviesBySelectedCategoriesDataSource.Selecting
'Set the @SelectedCategoryIDs parameter's value
'First, tally up the CategoryIDs from CategoryList
Dim CategoryIDList As New List(Of String)
For Each cb As ListItem In CategoryList.Items
If cb.Selected Then
'The Category was "checked"; add it's CategoryID value to CategoryIDList
CategoryIDList.Add(cb.Value)
End If
Next
'Create a comma-delimited string from CategoryIDList
Dim commaDelimitedString As String = String.Join(",", CategoryIDList.ToArray())
'Set the parameter value
e.Command.Parameters("@SelectedCategoryIDs").Value = commaDelimitedString
End Sub
End Class