我对SQL Server还是很陌生,这可能是我遇到此语法错误的原因。
我的代码:
CREATE PROCEDURE spBaseVoterIndex
@order_col NVARCHAR(100) ,
@order_dir NVARCHAR(20) ,
@offset ITN,
@limit INT
AS
SET NOCOUNT ON;
BEGIN
DECLARE @sql NVARCHAR(MAX)
SET @sql = 'SELECT id, name_voter, home_street_address_1, home_address_city FROM dbo.base_voter'
+' WITH(NOLOCK)'
+' ORDER BY @OC @OD'
+' OFFSET @OF ROWS'
+' FETCH NEXT @LIM ROWS ONLY'
EXECUTE sp_executesql @sql,
N'@OC nvarchar(191),@OD nvarchar(10),@OF int,@LIM int',
@OC @order_col, @OD @order_dir, @OF @offset, @LIM @limit
END
错误:
消息102,级别15,状态1,过程spBaseVoterIndex,第18行[批处理开始第0行]
'@order_col'附近的语法不正确。
我在这里做错了什么。另外,这是执行动态SQL的最佳方法还是其他优化方法?
打印@SQL
会导致
SELECT id, name_voter, home_street_address_1, home_address_city
FROM dbo.base_voter WITH(NOLOCK)
ORDER BY @OC @OD
OFFSET @OF ROWS
FETCH NEXT @LIM ROWS ONLY
我可以使用此代码
SELECT id,
name_voter,
home_street_address_1,
home_address_city
FROM dbo.base_voter
WITH(NOLOCK)
WHERE deleted_at IS NULL
order by name_voter asc
OFFSET 0 ROWS
FETCH NEXT 50 ROWS ONLY
所有我想使其动态化,其中name_voter asc
和偏移值0
和极限值50
这是我要使其动态化的4个参数。
有1亿行数据,因此性能也很重要。
答案 0 :(得分:0)
您似乎在该行中缺少@OC和@OD之间的逗号 +'ORDER BY @OC @OD'
您是否还注意到参数的大小在过程定义和动态SQL定义之间发生了变化?
您可以通过将动态SQL参数替换为实际参数,然后简单地打印最终字符串然后运行它来进行测试。
除了确保您知道使用NOLOCK可能引起的问题(脏读取,幻像读取,重复数据)外,您的用法看起来还有效。
答案 1 :(得分:0)
尝试一下。
CREATE PROCEDURE spBaseVoterIndex
@order_col NVARCHAR(100) ,
@order_dir NVARCHAR(20) ,
@offset INT,
@limit INT
AS
SET NOCOUNT ON;
BEGIN
DECLARE @sql NVARCHAR(MAX)
SET @sql = 'SELECT id, name_voter, home_street_address_1, home_address_city FROM dbo.base_voter'
+' WITH(NOLOCK)'
+' ORDER BY @OC @OD'
+' OFFSET @OF ROWS'
+' FETCH NEXT @LIM ROWS ONLY'
EXECUTE sp_executesql @sql,
N'@OC nvarchar(100),@OD nvarchar(20),@OF int,@LIM int',
@OC=@order_col, @OD=@order_dir, @OF=@offset, @LIM=@limit
END
答案 2 :(得分:0)
您需要将参数分配给sp_executesql的变量。 (您已经声明了它,但未指定它(忘记使用等号)。
CREATE PROCEDURE spBaseVoterIndex
@order_col NVARCHAR(191) ,
@order_dir NVARCHAR(10) ,
@offset INT,
@limit INT
AS
SET NOCOUNT ON;
BEGIN
DECLARE @sql NVARCHAR(MAX)
SET @sql = 'SELECT id, name_voter, home_street_address_1, home_address_city FROM dbo.base_voter'
+' WITH(NOLOCK)'
+' ORDER BY @order_col @order_dir'
+' OFFSET @offset ROWS'
+' FETCH NEXT @limit ROWS ONLY'
SET @sql = REPLACE(@sql,'@order_col',@order_col)
SET @sql = REPLACE(@sql,'@order_dir',@order_dir)
SET @sql = REPLACE(@sql,'@offset',@offset)
SET @sql = REPLACE(@sql,'@limit',@limit)
EXECUTE sp_executesql @sql
END
BTW在非动态SQL中,OFFSET
和FETCH NEXT
接受INT变量。因此,您可以例如直接使用它:
SELECT
id
, name_voter
, home_street_address_1
, home_address_city
FROM
dbo.base_voter
ORDER BY id
OFFSET @offset ROWS
FETCH NEXT @limit ROWS ONLY
但是,这将迫使您使用固定顺序(顺便说一句,这不是真正的问题)。如果需要使用order by,可以使用ROW_NUMBER
方法,如下所示:
SELECT * FROM (
SELECT
id
, name_voter
, home_street_address_1
, home_address_city
, ROW_NUMBER() OVER(ORDER BY @order_col + @order_dir) RN
FROM
base_voter
) D
WHERE
RN > @Offset
AND RN <= @Limit + @Offset
这将为您提供OFFSET
和FETCH NEXT
的相同结果
希望有一天会有所帮助。
答案 3 :(得分:0)
这种方式行不通-您无法像这样对ASC进行参数设置。您需要从字面上串联字符串。
添加一些字符串卫生措施以停止SQL注入是一个好主意
CREATE PROCEDURE spBaseVoterIndex
@order_col NVARCHAR(100) ,
@descending INT,
@offset INT,
@limit INT
AS
SET NOCOUNT ON;
BEGIN
DECLARE @sql NVARCHAR(MAX)
DECLARE @OD varchar(4) = 'ASC'
-- Default is ascending. Pass in 1 to order descending
IF @descending = 1 THEN SET @OD='DESC'
-- If the order column is not valid, exit
-- You need to put a list of valid columns in here
IF @order_col NOT IN ('column1','column2','column3') RETURN
-- Finally build and run the SQL
SET @sql = 'SELECT id, name_voter, home_street_address_1, home_address_city'
+' FROM dbo.base_voter'
+' WITH(NOLOCK)'
+' ORDER BY ' + @order_col + ' ' + @OD + ' '
+' OFFSET ' + CAST(@offset AS VARCHAR(20)) + ' ROWS'
+' FETCH NEXT ' + CAST(@limit AS VARCHAR(20)) + ' ROWS ONLY'
EXEC(@sql)
END
关于性能,这包括两个部分: