How to delete 1000 rows at a time with SQL Server OPENQUERY - sql-server

I need to delete rows in a link server's table, 1000 rows at a time.
My code is the following:
WHILE (SELECT COUNT(*) FROM OPENQUERY (SqlServerAcc, 'SELECT * FROM titles ') > 1000)
BEGIN
DELETE OPENQUERY (SqlServerAcc,
'SELECT * FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY Id) AS RowNumber FROM tbl) t WHERE RowNumber <= 1000');
END
But I do have errors:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near '>'.
at the expression > 1000.
I don't understand why this does not work.
Bonus: how can we make the name of the server not hard-coded; in PRD, it should be SqlServerPrd, but it seems that OPENQUERY does not accept variables for its arguments...

Related

Query sql statistics without SET parameter

I try to capture some statistic parameters for logging purpose. "SET parameters" are no option (i.e. set statistics time on).
So I tried to query some DMV:
select '3AAAAAAAAAAA';
--no GO-statement here
select
total_worker_time/execution_count AS [Avg CPU Time],
total_elapsed_time as [Elapsed Time],
total_rows as [Total rows],
st.text,
(select cast(text as varchar(4000)) from ::fn_get_sql((select sql_handle from sys.sysprocesses where spid = ##spid)))
from sys.dm_exec_query_stats AS qs
cross apply sys.dm_exec_sql_text(qs.sql_handle) AS st
--where ???
order by creation_time desc
The information captured here is almost what I need - but:
The query is only listed in the result of the DMV when it run in last executed GO-Block (not in the actual one). This is not what I need. I need something like ##error or ##rowcount what is available within the same GO-block and holds the elapsed time and CPU time. Any ideas how to query this information of the last statment?
If this can be solved: I would like to query the "last" statement execution within the session (##spid) without writing the statement twice.
Update on question:
This query is working "per session" and would list the requested values (although tirvial querys are missing). Top 1 would always bring back the values of the last Statement (not true if fired via exec #SQL what produces anonther session):
print 'hello';
select top 10 'my personal identifier: 1', * FROM sys.messages;
select top 20 'my personal identifier: 2', * FROM sys.messages;
print 'hello';
select 'hello';
select top 30 'my personal identifier: 3', * FROM sys.tables;
select top 1
total_worker_time/execution_count AS [Avg CPU Time],
total_elapsed_time as [Elapsed Time],
total_rows as [Total rows],
substring(st.text, (qs.statement_start_offset / 2) + 1, (case when qs.statement_end_offset = -1 then datalength(st.text) else qs.statement_end_offset end - qs.statement_start_offset ) / 2 + 5) as [executing statement]
from sys.dm_exec_query_stats AS qs
cross apply sys.dm_exec_sql_text(qs.sql_handle) AS st
where st.text = (select cast(text as varchar(4000)) from ::fn_get_sql((select sql_handle from sys.sysprocesses where spid = ##spid)))
order by qs.statement_start_offset desc;
The filter (where-clause) seems to be crude and not very robust. Is there any way to improve this?
I try to answer myself (Jeroen Mostert - Thank you very much for your help!) - the question is unanswered (see below):
The follwing function should give you CPU, execution time, I/O, number or rows of the last statement that was executed in the actual session, if the statement is complex enough to invoke a SQL plan generation. That is, after simple print commands the resultset would be enpty. Even so after the execution of stored procedures if they open a new session (i.e. after exec sp_executesql the resultset will be empty).
For the "average" SQL-Statement the following query should result in a rowset holding the information that you would otherwise get via set statistice time on and set statistice io on.
drop function if exists dbo.ufn_lastsql_resources ;
go
CREATE FUNCTION dbo.ufn_lastsql_resources (#session_id int)
RETURNS TABLE
AS
return
select
top 1
convert(char(10), getdate(), 121) + ' ' + substring(convert(char(40), getdate(), 121), 12,12) + ',' as [Time stamp],
cast(cast((last_worker_time / execution_count / 1000. ) as numeric(9,2)) as varchar(100)) + ',' as [Avg CPU Time in ms],
cast(cast((last_elapsed_time / 1000. ) as numeric(9,2)) as varchar(100)) + ',' as [Elapsed Time in ms],
cast(last_rows as varchar(100)) + ',' as [Total rows],
cast(substring(st.text, (statement_start_offset / 2) + 1, (case when statement_end_offset = -1 then datalength(st.text) else statement_end_offset end - statement_start_offset ) / 2 + 2) as varchar(4000)) + ','
as [executing statement],
last_physical_reads + last_logical_reads as [Reads],
last_logical_writes as [Writes],
--last_grant_kb,
--last_used_grant_kb,
--last_ideal_grant_kb,
--last_reserved_threads,
--last_used_threads
#session_id as spid
from
(
select qs.*
from sys.dm_exec_query_stats as qs
inner join sys.dm_exec_requests as eq
on qs.sql_handle = eq.sql_handle
and qs.plan_handle = eq.plan_handle
and eq.session_id = #session_id
) a
cross apply sys.dm_exec_sql_text(a.sql_handle) AS st
where
substring(st.text, (statement_start_offset / 2) + 1, (case when statement_end_offset = -1 then datalength(st.text) else statement_end_offset end - statement_start_offset ) / 2 + 2) not like '%ufn_lastsql_resources%'
order by
last_execution_time desc, statement_start_offset desc
go
Most probably there are more elegant ways to do so. Maybe it is possible to write something that will work properly even with statements that use an option (recompile) or on exec (#sql) Anyway: I seems to work on SQL Server 2016 and 2012. You need VIEW SERVER STATE permission on the Server. To invoke the function, try:
drop table if exists #t1
select top 10 'statement 1' a, * into #t1 from sys.messages
select 1, * from dbo.ufn_lastsql_resources(##spid) option (recompile)
drop table if exists #t2
select top 20 'statement 2' a, * into #t2 from sys.messages
--select 2, * from dbo.ufn_lastsql_resources(##spid)
select top 3 'statement 3' a, * from sys.messages
select 3, * from dbo.ufn_lastsql_resources(##spid) option (recompile)
The question remins unanswered, as the way is not working properly. It is not sure to catch the right statement out of the batch (top 1 within the session ordered by last_execution time and last in batch. This seems to be the wrong order. As the plans are reused this is the only way, I figured out to work.)

Why SQL Server doesn't see database

I'm implementing full-text search on SQL Server 2014. I've added dbo.Blocks table like dbo.Articles in the 4th step in this tutorial http://www.mikesdotnetting.com/article/298/implementing-sql-server-full-text-search-in-an-asp-net-mvc-web-application-with-entity-framework
And I have simplified Search procedure for my purposes:
CREATE PROCEDURE Search
#SearchTerm varchar(8000),
#CurrentPage int = 1,
#PageSize int = 20
AS
BEGIN
DECLARE #NearPredicate varchar(8000),
#AndPredicate varchar(8000),
#TotalRecords int
SELECT
#NearPredicate = COALESCE(#NearPredicate + ' NEAR ', '') + items
FROM
dbo.Split(#SearchTerm, ' ')
LEFT JOIN
sys.fulltext_system_stopwords ON items = stopword
WHERE
stopword IS NULL
SET #AndPredicate = REPLACE(#NearPredicate, 'NEAR', 'AND')
SET #NearPredicate = '(' + #NearPredicate + ')'
SET #TotalRecords = (
SELECT
COUNT(*)
FROM
AbstractBuildBlocks
WHERE CONTAINS(*, #AndPredicate )
)
SELECT a.SiteId,
a.Content,
ct.Rank,
#TotalRecords AS TotalRecords
FROM
AbstractBuildBlocks a
INNER JOIN CONTAINSTABLE (Blocks, *, #NearPredicate ) AS ct ON a.Id = ct.[Key]
ORDER BY
ct.RANK DESC
OFFSET (#CurrentPage - 1) * #PageSize ROWS FETCH NEXT #PageSize ROWS only
END
I try to test this in SQL Server Management Studio: press new query, print EXEC Search #SearchTerm="sample", #CurrentPage = 1 and hit !Execute.
There are two problems:
Small: SQL Server Management Studio underlines Search in my query and says:
Couldn't find stored procedure 'Search'
Big: server when executing query throws an error:
Msg 208, Level 16, State 1, Procedure Search, Line 34
Invalid object name 'Blocks'.
on line 34:
FROM dbo.Split(#SearchTerm, ' ') /*line 34*/
I found it strange because I've created database dbo.Blocks and in Split Function I even don't use this table.
Don't know if it important but dbo.Split is underlined with red, message: Invalid object name, but despite this procedure has been successfully created and stored.
And the last I've just try to replace Blocks with dbo.Blocks
here:
FROM
AbstractBuildBlocks a
INNER JOIN CONTAINSTABLE (Blocks, *, #NearPredicate ) AS ct ON a.Id = ct.[Key]
and recompile procedure - the error now:
Msg 208, Level 16, State 1, Procedure Search, Line 34
Invalid object name 'dbo.Blocks'. /instead of blocks/
UPDATE I've used this
SELECT '['+SCHEMA_NAME(schema_id)+'].['+name+']'
AS SchemaTable
FROM sys.tables
There is no Blocks table in result. And this is Blocks's properties
You're trying to use the Blocks object as a table but it's the name of your full text catalog. If you look at the CONTAINSTABLE documentation, the first parameter is a table that has been full text indexed. Should that first parameter be dbo.Search?

Issue creating function with a "with"

I am trying to create this function(variable names changed so allow naming mistakes as when tested original without function works fine),
CREATE FUNCTION [dbo].[GetQuality](#FruitID VARCHAR(200))
RETURNS varchar(200)
AS
BEGIN
DECLARE #Result varchar(200)
SET #Result = (
WITH
latest AS
(
SELECT * FROM (SELECT TOP 1 * FROM Fruits_Crate WHERE FruitID like #FruitID ORDER BY ExpiryDate DESC) a
),
result AS
(
SELECT
latest.ExpiryDate as LatestExpiryDate, latest.Size as LatestSize, latest.Weight as LatestWeight,
previous.ExpiryDate as PreviousExpiryDate, previous.Size as PreviousSize, previous.Weight as PreviousWeight,
CASE SIGN((latest.Weight * latest.Size) - (previous.Weight * previous.Size))
WHEN 1 THEN 'Increased'
WHEN 0 THEN 'Static'
WHEN -1 THEN 'Decreased'
ELSE 'No Previous Data'
END AS Movement
FROM (SELECT TOP 1 * FROM (SELECT TOP 2 * FROM Fruits_Crate WHERE FruitID like #FruitID ORDER BY ExpiryDate DESC) x ORDER BY ExpiryDate) previous
FULL OUTER JOIN latest ON previous.FruitID = latest.FruitID
)
SELECT result.Movement AS FruitMovement from result;
)
RETURN #Result
END
Error
Msg 156, Level 15, State 1, Procedure GetQuality, Line 10
Incorrect syntax near the keyword 'WITH'.
Msg 319, Level 15, State 1, Procedure GetQuality, Line 10
Incorrect syntax near the keyword 'with'. If this statement is a common table expression, an xmlnamespaces clause or a change tracking context clause, the previous statement must be terminated with a semicolon.
Msg 102, Level 15, State 1, Procedure GetQuality, Line 14
Incorrect syntax near ','. Msg 102, Level 15, State 1, Procedure GetQuality, Line 31 Incorrect syntax near ')'.
I think you can move the assignment down, and put a semi colon before the with, and all should be good.
CREATE FUNCTION [dbo].[GetQuality](#FruitID VARCHAR(200))
RETURNS varchar(200)
AS
BEGIN
DECLARE #Result varchar(200);
WITH
latest AS
(
SELECT * FROM (SELECT TOP 1 * FROM Fruits_Crate WHERE FruitID like #FruitID ORDER BY ExpiryDate DESC) a
),
result AS
(
SELECT
latest.ExpiryDate as LatestExpiryDate, latest.Size as LatestSize, latest.Weight as LatestWeight,
previous.ExpiryDate as PreviousExpiryDate, previous.Size as PreviousSize, previous.Weight as PreviousWeight,
CASE SIGN((latest.Weight * latest.Size) - (previous.Weight * previous.Size))
WHEN 1 THEN 'Increased'
WHEN 0 THEN 'Static'
WHEN -1 THEN 'Decreased'
ELSE 'No Previous Data'
END AS Movement
FROM (SELECT TOP 1 * FROM (SELECT TOP 2 * FROM Fruits_Crate WHERE FruitID like #FruitID ORDER BY ExpiryDate DESC) x ORDER BY ExpiryDate) previous
FULL OUTER JOIN latest ON previous.FruitID = latest.FruitID
)
SELECT #RESULT = result.Movement AS FruitMovement from result;
RETURN #Result
END

Syntax error when assign a variable for top clause in sql server

I need to get top 10, top 100 records like this from a table. But I need to assign the value for top clause such as 10 or 100 in a variable.
But when I give like below, it gives an syntax error "Incorrect syntax near '#numberofRecords'.".
DECLARE #numberofRecords INT
SELECT #numberofRecords = ConfigValue FROM tblConfigItems (NOLOCK) WHERE configName = 'TopRecords'
SELECT TOP #numberofRecords [ID],TypeID, MessageType, operationDate
FROM NotifyTbl (NOLOCK)
wHERE STATUS IN ('1', '2')
How to achieve this?
Avoid dinamic SQL. Just use this:
SELECT TOP (#numberofRecords) [ID],TypeID, MessageType, operationDate
FROM NotifyTbl (NOLOCK)
wHERE STATUS IN ('1', '2')
You need at least SQL 2005 to have it working. Yes () does the trick.
You could use dynamic SQL
exec('SELECT TOP ' + #numberofRecords + ' [ID],TypeID, MessageType, operationDate
FROM NotifyTbl (NOLOCK)
wHERE STATUS IN (''1'', ''2'')')

SQL server can not accept median function

I wrote this Median function but it is executing with errors.. Can someone guide me what's wrong in the code?
BEGIN
CREATE TABLE #ITEMORDERDETAILS
(
ITEM CHAR(15),
QTYSHP DECIMAL(21, 6),
RQDATE DATETIME
)
DECLARE #Median FLOAT
DECLARE #ITEM CHAR(15)
DECLARE #ORDERCNT INT
SET #ITEM=#ITEMN
INSERT #ITEMORDERDETAILS
SELECT ITEM,
QTYSHP,
RQDATE
FROM tbl123456
WHERE PRICE != '0'
AND SALESMN != 'WB'
AND RQDATE > ( getdate () - 180 )
AND ITEM = #ITEM
UNION
SELECT ITEM,
QTYSHP,
RQDATE
FROM tbl123
WHERE PRICE != '0'
AND SALESMN != 'WB'
AND RQDATE > ( getdate () - 180 )
AND ITEM = #ITEM
SELECT #ORDERCNT = count (1)
FROM #ITEMORDERDETAILS
--SELECT #ORDERCNT
SELECT #Median = ( sum(QTYSHP) / #ORDERCNT )
FROM #ITEMORDERDETAILS
SELECT #Median AS 'Median'
--SELECT * from #ITEMORDERDETAILS
DROP TABLE #ITEMORDERDETAILS
RETURN #Median
END
ERRORS
Msg 2772, Level 16, State 1, Procedure
f_Get_Average_Order_Size_Median, Line 34 Cannot access temporary
tables from within a function.
Msg 2772, Level 16, State 1, Procedure
f_Get_Average_Order_Size_Median, Line 35 Cannot access temporary
tables from within a function.
Msg 2772, Level 16, State 1, Procedure
f_Get_Average_Order_Size_Median, Line 42 Cannot access temporary
tables from within a function.
Msg 156, Level 15, State 1, Procedure
f_Get_Average_Order_Size_Median, Line 46 Incorrect syntax near the
keyword 'SELECT'.
The reason is in your error message:
Line 34 Cannot access temporary tables from within a function
If you make a function, there are limits to what you can access.
However, if you use SQL Server 2012, you do not need to write your own median function but you can use PERCENTILE_DISC
PERCENTILE_DISC (0.5) WITHIN GROUP (ORDER BY XXXX)
OVER (PARTITION BY YYYY) AS Median

Resources