Передача параметра SQL в предложение IN () с использованием типизированных наборов данных в .NET - PullRequest
13 голосов
/ 23 марта 2011

Первые извинения за то, что на этом сайте есть похожие вопросы, но ни один из них не решает эту проблему напрямую.

Я использую типизированные наборы данных в VS 2010. Я создаю TableAdapter в наборе данных с запросом типа:

SELECT * from Table WHERE ID IN(@IDs)

Теперь, если я позвоню: TableAdapter.Fill(MyDataTable,"1,2,3"), произойдет ошибка, сообщающая, что VS не может преобразовать 1,2,3 в тип int.Достаточно справедливо.

Итак, я решил изменить тип параметра (т.е. @IDs) на строку в коллекции параметров.Попробуйте еще раз - все то же сообщение об ошибке.

Так есть ли способ, которым этот типизированный набор данных может принять мой параметр "1,2,3"?На данный момент у меня есть только несколько параметров для передачи, так что я мог бы просто создать около 5 параметров и передать их отдельно, но что, если есть сотни?Можно ли как-нибудь вызвать метод Fill() с моим разделенным запятыми параметром?

(я знаю, что могу использовать динамический SQL для создания оператора и его выполнения, но предпочел бы, если есть другой способ, позволяющий мнеоставьте мой набранный набор данных для использования, например, в ReportViewer / bindingsources)

Ответы [ 8 ]

10 голосов
/ 23 марта 2011

Вы не можете использовать один параметр для списка значений таким способом. Но могут быть специфичные для базы данных способы достижения того, чего вы хотите. Например, в SQL Server 2005 или более поздней версии вы можете создать табличную функцию для разделения вашего строкового параметра, например:

CREATE FUNCTION dbo.F_Split
(
@InputString VARCHAR(MAX)
,@Separator VARCHAR(MAX)
)
RETURNS @ValueTable TABLE (Value VARCHAR(MAX))
AS
BEGIN

    DECLARE @SeparatorIndex INT, @TotalLength INT, @StartIndex INT, @Value VARCHAR(MAX)
    SET @TotalLength=LEN(@InputString)
    SET @StartIndex = 1

    IF @Separator IS NULL RETURN

    WHILE @StartIndex <= @TotalLength
    BEGIN
        SET @SeparatorIndex = CHARINDEX(@Separator, @InputString, @StartIndex)
        IF @SeparatorIndex > 0
        BEGIN
            SET @Value = SUBSTRING(@InputString, @StartIndex, @SeparatorIndex-@StartIndex)
            SET @StartIndex = @SeparatorIndex + 1
        END
        ELSE
        BEGIN
            Set @Value = SUBSTRING(@InputString, @StartIndex, @TotalLength-@StartIndex+1)
            SET @StartIndex = @TotalLength+1
        END
        INSERT INTO @ValueTable
        (Value)
        VALUES
        (@Value)
    END

    RETURN
END

Затем вы бы использовали его следующим образом:

SELECT * from Table WHERE ID IN (SELECT CAST(Value AS INT) FROM F_Split(@IDs, ','))
9 голосов
/ 10 сентября 2013

Я попробовал обходной путь для использования понятия «содержит» строку в SQL-способе:

В вашем случае измените SQL -

Оригинал:

SELECT* из таблицы WHERE ID IN (@IDs)

Стать:

SELECT * из таблицы WHERE CharIndex (',' + Cast (ID As Varchar (10)) + ',', @ IDs)> 0

с кодом .net -

Оригинал:

TableAdapter.Fill (MyDataTable, "1,2,3")

Стать:

TableAdapter.Fill (MyDataTable, ", 1,2,3, ")

6 голосов
/ 23 марта 2011

SQL Server 2008 имеет функцию под названием Табличные параметры

Так что вам нужно

  1. определите ваш запрос как SELECT * from Table WHERE ID IN (SELECT * FROM (@IDs))
  2. вернитесь в визуальный конструктор TableAdapter в Visual Studio и обновите параметр @IDS, чтобы изменить параметр @IDS как DbType = Object и ProviderType = Structured
  3. запустить этот пакет SQL в базе данных, которую вы используете: CREATE TYPE MyIntArray AS TABLE ( Value INT );GO. Это создаст MyIntArray «тип таблицы» с одним столбцом типа INT.
  4. Теперь сложно передать тип «MyIntArray» в TableAdapter на стороне ADO.NET.

К сожалению, дизайнер Table Adapter не поддерживает аргумент SqlParameter.TypeName, поэтому нам нужно исправить это самостоятельно. Цель состоит в том, чтобы изменить свойство CommandCollection созданного класса TableAdapter. К сожалению, это свойство защищено, поэтому вам нужно извлечь TableAdapter или, например, использовать Reflection для его настройки. Вот пример с производным классом:

    public class MyTableAdapter2 : MyTableAdapter
    {
        public MyTableAdapter2()
        {
            SqlCommand[] cmds = base.CommandCollection;
            // here, the IDS parameter is index 0 of command 1
            // you'll have to be more clever, but you get the idea
            cmds[1].Parameters[0].TypeName = "MyIntArray";
        }
    }

И вот как вы можете вызвать этот метод:

        MyTableAdapter t = new MyTableAdapter2();

        // create the TVP parameter, with one column. the name is irrelevant.
        DataTable tvp = new DataTable();
        tvp.Columns.Add();

        // add one row for each value
        DataRow row = tvp.NewRow();
        row[0] = 1;
        tvp.Rows.Add(row);

        row = tvp.NewRow();
        row[0] = 2;
        tvp.Rows.Add(row);

        row = tvp.NewRow();
        row[0] = 3;
        tvp.Rows.Add(row);

        t.Fill(new MyDataTable(), tvp);
1 голос
/ 23 марта 2011

Единственная известная мне база данных, которая может использовать параметры из .NET в предложении IN, - это PostgreSQL, поскольку в PostgreSQL есть концепция массивов, которые можно использовать с IN, а Npgsql допускает массив (или IEnumerable<T>) параметры.

С другими базами данных вам нужно либо создать SQL, либо передать строку в процедуру базы данных, которая преобразует ее в 0 или более параметров и затем воздействует на них.

0 голосов
/ 20 апреля 2018

Я смог решить эту проблему, установив для свойства ClearBeforeFill значение false и заполнив TableAdapter в цикле foreach.

List<string> aList = new List<string>();
aList.Add("1");
aList.Add("2");
aList.Add("3");

yourTableAdapter.ClearBeforeFill = true;
yourTableAdapter.Fill(yourDataSet.yourTableName, ""); //clears table

foreach (string a in aList)
{
    yourTableAdapter.ClearBeforeFill = false;
    yourTableAdapter.Fill(yourDataSet.yourTableName, a);
}
yourTableAdapter.Dispose();
0 голосов
/ 20 августа 2014

Вы также можете использовать XML для передачи списка параметров в хранимую процедуру:

1) В Visual Studio:

Создать новый адаптер таблицы и создатьВводим набор данных, чтобы получить одну запись:

SELECT * FROM myTable WHERE (ID = @ID)

2) В SQL Server Manager:

Создайте хранимую процедуру с теми же полями выбора, что и для вашего набранного набора данных:

CREATE PROCEDURE [dbo].[usrsp_GetIds]
    @paramList xml = NULL
AS
    SET NOCOUNT ON;

/*
Create a temp table to hold paramaters list.
Parse XML string and insert each value into table.
Param list contains: List of ID's
*/
DECLARE @tblParams AS TABLE (ID INT)
INSERT INTO @tblParams(ID) 
    SELECT 
        XmlValues.ID.value('.', 'INT')
    FROM 
        @paramList.nodes('/params/value') AS XmlValues(ID)

/* 
Select records that match ID's in param list:
*/
SELECT * FROM myTable 
WHERE 
    ID IN (
        SELECT ID FROM @tblParams
    )

3) В Visual Studio:

Добавьте новый запрос к табличному адаптеру, выберите хранимую процедуру, созданную выше usrsp_GetIds , и назовите ее FillBy_Ids .Это создает команду:

TableAdapter.FillBy_Ids(@paramList)

4) В Visual Studio:

. В вашем коде .net создайте служебную функцию для преобразования массива строк в XML:

    ''' <summary>
    ''' Converts an array of strings to XML values.
    ''' </summary>
    ''' <remarks>Used to pass parameter values to the data store.</remarks>
    Public Shared Function ConvertToXML(xmlRootName As String, values() As String) As String
        'Note: XML values must be HTML encoded.
        Dim sb As New StringBuilder
        sb.AppendFormat("<{0}>", HttpUtility.HtmlEncode(xmlRootName))
        For Each value As String In values
            sb.AppendLine()
            sb.Append(vbTab)
            sb.AppendFormat("<value>{0}</value>", HttpUtility.HtmlEncode(value))
        Next
        sb.AppendLine()
        sb.AppendFormat("</{0}>", xmlRootName)
        Return sb.ToString
    End Function

Пример использования:

Заполните таблицу данных, используя строго типизированные функции, передав список строк в качестве параметра:

'Create a list of record IDs to retrieve:
Dim ids as New List(of String)
ids.Add(1)
ids.Add(2)
ids.Add(3)

'Convert the list of IDs to an XML string:
Dim paramsXml As String = ConvertToXML("params", ids.ToArray)

'Get the records using standard DataTable & TableAdapter methods:
Using myDT As New MyDataTable
    Using myTA As New MyTableAdapter

        myTA.FillBy_Ids(myDT, paramsXml)

        For Each row In myDT
            'do stuff:
        Next

    End Using
End Using
0 голосов
/ 09 марта 2012

Вы также можете создать список параметров идентификаторов, поэтому вместо использования @ID вы будете использовать @ ID1, @ ID2, @ ID3 и т.д.

var result = string.Empty;
            for (int i = 0; i < count; i++)
            {
                result += ", @ID" + i;
            }
            return string.IsNullOrEmpty(result) ? string.Empty : result.Substring(1);

и, наконец, добавьте параметры:

foreach (int i = 0; i < values.Count; i++)
            {
                cmd.Parameters.Add(new SqlParameter("@ID" + i, SqlDbType.VarChar) { Value = values[i]});
            }
0 голосов
/ 23 марта 2011

@ Джо прав.

Или вы можете использовать цикл foreach для этого.

Что-то вроде:

   int[] arr = new int[3]; 
    arr[0] = "1";        
    arr[1] = "2";             
    arr[2] = "3";             

foreach(vat data in arr)
{

//Do your Code here

// 
var MyDatatable = obj.GetDatabyID(data);

TableAdapter.Fill(MyDataTable);

}

Привет

...