#sql-server #sql-server-2005 #stored-procedures
#sql-сервер #sql-server-2005 #хранимые процедуры
Вопрос:
Мне нужно записать массив из 36 плавающих значений в базу данных SQL Server 2005. Существует ли рекомендуемый ответ, который объясняет, как это сделать?
Я не могу записать его в виде строки, потому что нам может потребоваться запрашивать отдельные значения по миллионам строк, и я не хочу разбирать каждую строку, чтобы прочитать ее значение.
Я также хотел бы избежать передачи 36 параметров в хранимую процедуру.
Итак, что рекомендует обычная мудрость StackOverflow?
Комментарии:
1. Обновитесь до SQL Server 2008 и используйте табличные параметры
Ответ №1:
В Sql Server 2008 параметры с табличными значениями используются именно по этой причине. Однако в 2005 году у вас есть только (1) объединение / разделение строк или (2) xml.
Я бы выбрал передачу XML, а затем вставил в табличную переменную. Вот пример:
declare @floatsXml nvarchar(max);
set @floatsXml = '<nums><num val="2.123" /><num val="2.123" /></nums>';
declare @floats table (value float);
insert into @floats
select tbl.c.value('@val', 'float')
from @floatsXml.nodes('/nums/num') as tbl(c);
select *
from @floats;
У меня нет текущей установки sql Server, поэтому мой синтаксис может быть немного неточным, но в основном он должен быть правильным.
Ответ №2:
В SQL Server 2008 вы могли бы использовать параметр с табличным значением. Поскольку это SQL 2005, я использую список, разделенный запятыми, а затем функцию разделения, которая преобразует к соответствующему типу данных и возвращает таблицу.
Это функция разделения, которую я использую:
IF OBJECT_ID('dbo.Nums') IS NOT NULL
DROP TABLE dbo.Nums ;
GO
CREATE TABLE [dbo].[Nums]
(
[n] int NOT NULL,
PRIMARY KEY CLUSTERED ([n] ASC)
WITH (PAD_INDEX = OFF, FILLFACTOR = 100, IGNORE_DUP_KEY = OFF,
STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
)
ON
[PRIMARY] ;
GO
DECLARE @max AS INT,
@rc AS INT ;
SET @max = 1000000 ;
SET @rc = 1 ;
INSERT INTO dbo.Nums (n)
VALUES (1) ;
WHILE @rc * 2 <= @max
BEGIN
INSERT INTO dbo.Nums (n)
SELECT n @rc
FROM dbo.Nums ;
SET @rc = @rc * 2 ;
END
INSERT INTO dbo.Nums (n)
SELECT n @rc
FROM dbo.Nums
WHERE n @rc <= @max ;
GO
CREATE FUNCTION [dbo].[fn_split]
(@arr nvarchar(4000), @sep nchar(1))
RETURNS table
AS
RETURN
SELECT (n - 1) - LEN(REPLACE(LEFT(@arr, n-1), @sep, N'')) 1 AS pos,
SUBSTRING(@arr, n, CHARINDEX(@sep, @arr @sep, n) - n) AS element
FROM dbo.Nums
WHERE n <= LEN(@arr) 1
AND SUBSTRING(@sep @arr, n, 1) = @sep;
GO
Комментарии:
1. Я как раз собирался опубликовать тот же ответ 🙂
Ответ №3:
Если бы это было 360 или 3600, я бы сказал XML или строку, но 36, вероятно, все еще достаточно мало, чтобы учитывать параметры.
Основная причина, по которой я говорю это, заключается в том, что когда вы переходите к тексту вместо строго типизированных параметров, вам приходится беспокоиться о последовательном форматировании. Форматирование чисел может быть сложным, поскольку в некоторых странах для разделителей тысяч используются запятые, а в других — десятичные разделители. Если ваш код клиентского доступа выполняется с другими региональными настройками, чем ожидалось от хранимой процедуры, вы можете обнаружить, что это приводит к сбою вашего приложения.
Ответ №4:
Я бы рекомендовал передать значения float в качестве параметра XML, но сохранить его в базе данных в 36 отдельных столбцах. (Я предполагаю здесь, что значения float отличаются по смыслу друг от друга и что вы захотите извлечь их по отдельности.)
Как вы сказали, вы не хотите разархивировать каждую строку для чтения ее значения, поэтому вам понадобится 36 столбцов. (Это тоже хорошая практика). Но вы можете передать значения с плавающей точкой в виде фрагмента XML, который вы можете разобрать различными способами, чтобы получить отдельные значения.
Комментарии:
1. Почему 36 одинаковых столбцов являются хорошей практикой? Звучит как кандидат на нормализацию. Я бы выбрал отдельную таблицу со столбцом index и value.
2. Обработка XML в SQL Server может быть очень ресурсоемкой. Обычно этого лучше избегать, если только вам не нужно его использовать.
Ответ №5:
Используйте это для передачи массива с помощью «create type table». простой пример для пользователя
CREATE TYPE unit_list AS TABLE (
ItemUnitId int,
Amount float,
IsPrimaryUnit bit
);
GO
CREATE TYPE specification_list AS TABLE (
ItemSpecificationMasterId int,
ItemSpecificationMasterValue varchar(255)
);
GO
declare @units unit_list;
insert into @units (ItemUnitId, Amount, IsPrimaryUnit)
values(12,10.50, false), 120,100.50, false), (1200,500.50, true);
declare @spec specification_list;
insert into @spec (ItemSpecificationMasterId,temSpecificationMasterValue)
values (12,'test'), (124,'testing value');
exec sp_add_item "mytests", false, @units, @spec
//Procedure definition
CREATE PROCEDURE sp_add_item
(
@Name nvarchar(50),
@IsProduct bit=false,
@UnitsArray unit_list READONLY,
@SpecificationsArray specification_list READONLY
)
AS
BEGIN
SET NOCOUNT OFF
print @Name;
print @IsProduct;
select * from @UnitsArray;
select * from @SpecificationsArray;
END