Передача списка целых чисел в хранимую процедуру SQL Server [duplicate]

Я согласен с ответом от zacherates.

Но вы можете сделать вызов intern () в ваших нелиберальных строках.

Из примера zacherates:

// ... but they are not the same object
new String("test") == "test" ==> false 

Если вы ставите нелитеральное равенство строки, это правда

new String("test").intern() == "test" ==> true 
223
задан Ragmar 19 October 2017 в 15:09
поделиться

8 ответов

Вам нужно передать его как параметр XML.

Изменить: быстрый код из моего проекта, чтобы дать вам идею:

CREATE PROCEDURE [dbo].[GetArrivalsReport]
    @DateTimeFrom AS DATETIME,
    @DateTimeTo AS DATETIME,
    @HostIds AS XML(xsdArrayOfULong)
AS
BEGIN
    DECLARE @hosts TABLE (HostId BIGINT)

    INSERT INTO @hosts
        SELECT arrayOfUlong.HostId.value('.','bigint') data
        FROM @HostIds.nodes('/arrayOfUlong/u') as arrayOfUlong(HostId)

Затем вы можете использовать временную таблицу присоединиться к вашим столам. Мы определили arrayOfUlong как построенную в XML-схему для обеспечения целостности данных, но вам не нужно это делать. Я бы рекомендовал использовать его, так что вот быстрый код, чтобы убедиться, что вы всегда получаете XML с longs.

IF NOT EXISTS (SELECT * FROM sys.xml_schema_collections WHERE name = 'xsdArrayOfULong')
BEGIN
    CREATE XML SCHEMA COLLECTION [dbo].[xsdArrayOfULong]
    AS N'<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="arrayOfUlong">
        <xs:complexType>
            <xs:sequence>
                <xs:element maxOccurs="unbounded"
                            name="u"
                            type="xs:unsignedLong" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>';
END
GO
356
ответ дан Aaron Bertrand 16 August 2018 в 01:17
поделиться
  • 1
    Я думал, что было плохой идеей использовать переменные таблицы, когда есть много строк? Не лучше ли использовать вместо этого таблицу temp (#table)? – ganders 19 June 2012 в 18:32
  • 2
    Мне нравится идея параметра таблицы - никогда не задумывалась об этом - приветствия. Для чего нужно, чтобы разделитель переходил в вызов функции SplitInts (). – Drammy 9 May 2013 в 23:35
  • 3
    Как я могу использовать параметр таблицы, если только имеет доступ к строке, разделенной запятой – bdwain 23 May 2013 в 19:01
  • 4
    @bdwain победит цель - вам придется использовать функцию split, чтобы разбить ее на строки, чтобы поместить в TVP. Разбейте его в своем коде приложения. – Aaron Bertrand 23 May 2013 в 19:09
  • 5
    Это здорово, спасибо. Как передать параметры таблицы в виде строки, а не конструировать ее в C #? т. е. будет ли это @List = [(1, 'Jane') (2, 'Smith') (3, 'Chris')] – RobHurd 15 July 2014 в 01:57
  • 6
    @ganders: Я бы сказал наоборот. – abatishchev 24 December 2014 в 00:11
  • 7
    @ th1rdey3 Они неявно необязательны. stackoverflow.com/a/18926590/61305 – Aaron Bertrand 1 April 2015 в 14:30

Я просматривал все примеры и ответы о том, как передать любой массив на сервер sql без проблем с созданием нового типа таблицы, пока я не нашел это linK , ниже, как я применил это в мой проект:

- Следующий код будет получать Array как параметр и вставить значения этого --array в другую таблицу

Create Procedure Proc1 


@UserId int, //just an Id param
@s nvarchar(max)  //this is the array your going to pass from C# code to your Sproc

AS

    declare @xml xml

    set @xml = N'<root><r>' + replace(@s,',','</r><r>') + '</r></root>'

    Insert into UserRole (UserID,RoleID)
    select 
       @UserId [UserId], t.value('.','varchar(max)') as [RoleId]


    from @xml.nodes('//root/r') as a(t)
END 

Надеюсь, вам понравится это

5
ответ дан Adam 16 August 2018 в 01:17
поделиться
  • 1
    не знаю, почему это не поддерживается, это CLEANEST решение всех imo ... – zaitsman 28 August 2014 в 07:29
  • 2
    @zaitsman: CLEANEST не означает лучшее или самое подходящее. Один часто отказывается от гибкости и / или «подходит». сложность и / или производительность, чтобы получить «очистить». код. Этот ответ здесь "ok" но только для небольших наборов данных. Если входящий массив @s является CSV, тогда было бы быстрее просто разбить это (т. Е. INSERT INTO ... SELECT FROM SplitFunction). Преобразование в XML происходит медленнее, чем CLR, и XML-атрибут намного быстрее. И это простой список, но передача в XML или TVP также может обрабатывать сложные массивы. Не уверен, что получается, избегая простого, разового CREATE TYPE ... AS TABLE. – Solomon Rutzky 13 September 2014 в 22:23

Это поможет вам. :) Следуйте следующим шагам:

  1. Откройте конструктор запросов
  2. Копировать Вставьте следующий код так, как он есть, он создаст функцию, которая преобразует String в Int
    CREATE FUNCTION dbo.SplitInts
    (
       @List      VARCHAR(MAX),
       @Delimiter VARCHAR(255)
    )
    RETURNS TABLE
    AS
      RETURN ( SELECT Item = CONVERT(INT, Item) FROM
          ( SELECT Item = x.i.value('(./text())[1]', 'varchar(max)')
            FROM ( SELECT [XML] = CONVERT(XML, '<i>'
            + REPLACE(@List, @Delimiter, '</i><i>') + '</i>').query('.')
              ) AS a CROSS APPLY [XML].nodes('i') AS x(i) ) AS y
          WHERE Item IS NOT NULL
      );
    GO
    
  3. Создать следующую хранимую процедуру
     CREATE PROCEDURE dbo.sp_DeleteMultipleId
     @List VARCHAR(MAX)
     AS
     BEGIN
          SET NOCOUNT ON;
          DELETE FROM TableName WHERE Id IN( SELECT Id = Item FROM dbo.SplitInts(@List, ',')); 
     END
     GO
    
  4. Выполнить этот SP Используя exec sp_DeleteId '1,2,3,12', это строка идентификатора, которую вы хотите удалить,
  5. Вы преобразуйте свой массив в строку в C # и передайте его как параметр хранимой процедуры
    int[] intarray = { 1, 2, 3, 4, 5 };  
    string[] result = intarray.Select(x=>x.ToString()).ToArray();
    
    SqlCommand command = new SqlCommand();
    command.Connection = connection;
    command.CommandText = "sp_DeleteMultipleId";
    command.CommandType = CommandType.StoredProcedure;
    command.Parameters.Add("@Id",SqlDbType.VARCHAR).Value=result ;
    

Это приведет к удалению нескольких строк, Все лучшее

3
ответ дан Eduardo Cuomo 16 August 2018 в 01:17
поделиться
  • 1
    я использовал эту функцию раздельного разбора запятой, она будет работать для небольшого набора данных, если вы проверите его план выполнения, это вызовет проблему на большом наборе данных и где у вас будет несколько списков csv в хранимой процедуре – Saboor Awan 24 June 2016 в 05:59

Мне потребовалось много времени, чтобы понять это, поэтому на случай, если кому-то это понадобится ...

Это основано на методе SQL 2005 в ответе Аарона и использовании его функции SplitInts (я просто удалил delim param, так как я всегда буду использовать запятые). Я использую SQL 2008, но мне нужно что-то, что работает с типизированными наборами данных (XSD, TableAdapters), и я знаю, что строковые параметры работают с ними.

Я пытался заставить его функцию работать в "где (1,2,3) ", и не повезло прямолинейным способом. Поэтому я сначала создал временную таблицу, а затем сделал внутреннее соединение вместо «где в». Вот мой пример использования, в моем случае я хотел получить список рецептов, которые не содержат определенных ингредиентов:

CREATE PROCEDURE dbo.SOExample1
    (
    @excludeIngredientsString varchar(MAX) = ''
    )
AS
    /* Convert string to table of ints */
    DECLARE @excludeIngredients TABLE (ID int)
    insert into @excludeIngredients
    select ID = Item from dbo.SplitInts(@excludeIngredientsString)

    /* Select recipies that don't contain any ingredients in our excluded table */
   SELECT        r.Name, r.Slug
FROM            Recipes AS r LEFT OUTER JOIN
                         RecipeIngredients as ri inner join
                         @excludeIngredients as ei on ri.IngredientID = ei.ID
                         ON r.ID = ri.RecipeID
WHERE        (ri.RecipeID IS NULL)
2
ответ дан eselk 16 August 2018 в 01:17
поделиться
  • 1
    Вообще говоря, лучше не присоединяться к переменной таблицы, а вместо этого к таблице Temp. Переменные таблицы, по умолчанию, кажутся только одной строкой, хотя вокруг есть трюк или два (ознакомьтесь с превосходной и подробной статьей @ AaronBertrand: sqlperformance.com/2014/06/t-sql-queries/&hellip ; [/ д0]). – Solomon Rutzky 13 September 2014 в 23:06

Основываясь на моем опыте, создавая разделительное выражение из идентификаторов employeeID, для этой проблемы есть сложное и приятное решение. Вы должны создать только строковое выражение, подобное ';123;434;365;', в котором 123, 434 и 365 - некоторые идентификаторы employeeID. Вызвав приведенную ниже процедуру и передав ей это выражение, вы можете получить нужные записи. Вы легко можете присоединиться к «другой таблице» в этот запрос. Это решение подходит для всех версий SQL-сервера. Кроме того, по сравнению с использованием табличной переменной или таблицы temp это очень быстрое и оптимизированное решение.

CREATE PROCEDURE dbo.DoSomethingOnSomeEmployees  @List AS varchar(max)
AS
BEGIN
  SELECT EmployeeID 
  FROM EmployeesTable
  -- inner join AnotherTable on ...
  where @List like '%;'+cast(employeeID as varchar(20))+';%'
END
GO
27
ответ дан Hamed Nazaktabar 16 August 2018 в 01:17
поделиться
  • 1
    Ницца! Мне очень нравится такой подход, когда я фильтруюсь на int-ключах! +1 – MDV2000 22 April 2016 в 20:07
  • 2
    @ MDV2000 спасибо :) В строковых ключах это также имеет хорошую производительность из-за того, что не меняет int-столбцы на varchar ... – Hamed Nazaktabar 25 May 2016 в 05:05
  • 3
    Спасибо, это действительно сработало для меня. – ashkanr 11 December 2017 в 08:52

Используйте хранимый в таблице параметр для вашей хранимой процедуры.

Когда вы передаете его с C #, вы добавите параметр с типом данных SqlDb.Structured.

См. Здесь: http://msdn.microsoft.com/en-us/library/bb675163.aspx

Пример:

// Assumes connection is an open SqlConnection object.
using (connection)
{
// Create a DataTable with the modified rows.
DataTable addedCategories =
  CategoriesDataTable.GetChanges(DataRowState.Added);

// Configure the SqlCommand and SqlParameter.
SqlCommand insertCommand = new SqlCommand(
    "usp_InsertCategories", connection);
insertCommand.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParam = insertCommand.Parameters.AddWithValue(
    "@tvpNewCategories", addedCategories);
tvpParam.SqlDbType = SqlDbType.Structured;

// Execute the command.
insertCommand.ExecuteNonQuery();
}
21
ответ дан Levi W 16 August 2018 в 01:17
поделиться

В sql-сервере нет поддержки массива, но есть несколько способов, с помощью которых вы можете передать коллекцию в сохраненный процесс.

  1. Используя datatable
  2. Используя XML.Try конвертирует вашу коллекцию в формате xml, а затем передает ее как вход в хранимую процедуру

. Ниже ссылка может помочь вам

передать коллекцию к хранимой процедуре

6
ответ дан praveen 16 August 2018 в 01:17
поделиться

Контекст всегда важен, например size и сложность массива. Для небольших и средних списков несколько ответов, размещенных здесь, просто прекрасны, хотя некоторые разъяснения должны быть сделаны:

  • . Для разделения списка с разделителями самый сплиттер на основе SQLCLR является самым быстрым. Существует множество примеров, если вы хотите написать свой собственный или вы можете просто загрузить бесплатную библиотеку SQL # функций CLR (которую я написал, но функция String_Split и многие другие) полностью бесплатны ).
  • Разделение массивов на основе XML может быть быстрым, но вам нужно использовать XML на основе атрибутов, а не на основе элементов XML (который является единственным типом, указанным в ответах здесь, хотя XML-пример @ AaronBertrand является лучшим, поскольку его код использует функцию XML text(). Для получения дополнительной информации (например, анализа производительности) при использовании XML для разбиения списков проверьте «Использование XML для передачи списков в качестве параметров в SQL Server » Phil Factor.
  • Использование TVP отлично (если вы используете хотя бы SQL Server 2008 или новее) по мере того, как данные передаются в proc и отображаются пре- анализируется и строго типизируется как переменная таблицы. В любом случае сохранение всех данных в DataTable означает дублирование данных в памяти, поскольку они копируются из исходной коллекции. Следовательно, используя DataTable меня что передача в ТВП не работает хорошо для больших наборов данных (т. е. не очень хорошо масштабируется).
  • XML, в отличие от простых разделительных списков Ints или Strings, может обрабатывать более чем одномерные массивы, подобно TVP. Но также, как и метод DataTable TVP, XML не масштабируется, так как он более чем удваивает объем данных в памяти, поскольку ему необходимо дополнительно учитывать накладные расходы XML-документа.

При всем том, что, если данные, которые вы используете, являются большими или не очень большими, но постоянно растут, то метод IEnumerable TVP является лучшим выбором, поскольку он передает данные на SQL Server (например, метод DataTable ), НО не требует дублирования коллекции в памяти (в отличие от любого другого метода). В этом ответе я привел пример кода SQL и C #:

Пропустить словарь для хранимой процедуры T-SQL

10
ответ дан Solomon Rutzky 16 August 2018 в 01:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: