Database version - sql

I have to write a procedure which is able using a Table version to bring the database to a specific moment in type. For instance to move from version 1 to version 10 or the other way around. The thing is I'm pretty blurry with this chapter, and the school course has almost nothing about it. I tried using the internet to build a solution but somehow I got stuck. Please help me understand what am I doing wrong.
Table version, 1 columnm, type int
query
create procedure [dbo].[goto_vs] (
#vs int
)
as
begin
declare #current_vs int, #counter int;
declare #sqlquery nvarchar(50); --query to modify
declare #sqlsp nvarchar(30);
declare #sqlversion nvarchar(3);
declare #sqlreverse nvarchar(10);
--get the current version from table
select #current_vs=version from dbo.version;
--checking for valid version
if (#current_vs = #vs) begin
print('The database is already at this version...')
return
end
else begin
if (#vs > 5) begin
print('Setting the version of databse to last one...')
set #vs = 5
end
else begin
if (#vs < 0) begin
print('Setting the database to default...')
set #vs = 0
end
end
end
--setting up the string for exec
set #sqlsp = 'exec sp_create_table_awards'
--check if we go further or earlier in time
print('Changing database version...')
if (#vs > #current_vs) begin
set #sqlreverse = ''
goto upgrading
end
else begin
set #sqlreverse = 'undo_create_awards'
goto downgrading
end
--upgrading code
upgrading:
set #counter = #current_vs + 1
while (#counter <= #vs) begin
set #sqlquery = #sqlsp + cast(#counter as nvarchar(2)) + #sqlreverse
print(#sqlquery)
exec sp_executeSql #sqlquery
set #counter = #counter + 1
end
goto ending
downgrading:
set #counter = #current_vs
while (#counter > #vs) begin
set #sqlquery = #sqlsp + cast(#counter as nvarchar(2)) + #sqlreverse
print(#sqlquery)
exec sp_executeSql #sqlquery
set #counter = #counter - 1
end
goto ending
ending:
update dbo.version set version=#vs
print('Database version changed...')
end

Considering I figured a way, and have got no responses, I will post it as a response because it may help other students which studie computer science
To simplify I named all my procedures by this pattern do_x and undo_x where x is a int where do / undo _x are procedures which pair toughter for example do_1 and undo_1 create a table and destroy a table
ALTER PROCEDURE [dbo].[goto_vs]
-- Add the parameters for the stored procedure here
#v int = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
declare #i int
declare #toexec nvarchar(70)
set #i = (select version from version)
-- If the requested version is lower than the current one, downgrade.
while(#i > #v)
BEGIN
set #i = #i - 1
set #toexec = 'Undo_' + CONVERT(varchar,#i);
exec sp_executeSql #toexec
print #toexec
END
-- Otherwise, upgrade.
while(#i < #v)
BEGIN
set #toexec = 'Update_' + CONVERT(varchar, #i);
exec sp_executeSql #toexec
set #i = #i + 1
print #toexec
END
END

Related

Cursorfetch error because of wrong variable type

i try to do a benchmark for SQL Statments for SQLServer.
I found a good benchmark loop online: https://github.com/jOOQ/jOOQ/blob/master/jOOQ-examples/Benchmarks/SQLServer/Benchmarking%20SQL%20Server%20(absolute).sql
DECLARE #ts DATETIME;
DECLARE #repeat INT = 10000;
DECLARE #r INT;
DECLARE #i INT;
DECLARE #dummy VARCHAR;
DECLARE #s1 CURSOR;
DECLARE #s2 CURSOR;
SET #r = 0;
WHILE #r < 5
BEGIN
SET #r = #r + 1
SET #s1 = CURSOR FOR
-- Paste statement 1 here
SELECT 1 x;
SET #s2 = CURSOR FOR
-- Paste statement 2 here
WITH t(v) AS (
SELECT 1
UNION ALL
SELECT v + 1 FROM t WHERE v < 10
)
SELECT * FROM t
SET #ts = current_timestamp;
SET #i = 0;
WHILE #i < #repeat
BEGIN
SET #i = #i + 1
OPEN #s1;
FETCH NEXT FROM #s1 INTO #dummy;
WHILE ##FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM #s1 INTO #dummy;
END;
CLOSE #s1;
END;
DEALLOCATE #s1;
PRINT 'Run ' + CAST(#r AS VARCHAR) + ', Statement 1: ' + CAST(DATEDIFF(ms, #ts, current_timestamp) AS VARCHAR) + 'ms';
SET #ts = current_timestamp;
SET #i = 0;
WHILE #i < #repeat
BEGIN
SET #i = #i + 1
OPEN #s2;
FETCH NEXT FROM #s2 INTO #dummy;
WHILE ##FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM #s2 INTO #dummy;
END;
CLOSE #s2;
END;
DEALLOCATE #s2;
PRINT 'Run ' + CAST(#r AS VARCHAR) + ', Statement 2: ' + CAST(DATEDIFF(ms, #ts, current_timestamp) AS VARCHAR) + 'ms';
END;
PRINT '';
PRINT 'Copyright Data Geekery GmbH';
PRINT 'https://www.jooq.org/benchmark';
This works great for when the statments i test only have one column they return. For example:
Select ID from Items Where ID=2;
But as soon as i try to select multiple rows like
Select * from Items Where ID=2;
i get the error:
Msg 16924, Level 16, State 1, Line 135 Cursorfetch: The number of
variables declared in the INTO list must match that of selected
columns.
So the column this concerns is
FETCH NEXT FROM #s1 INTO #dummy;
So as far as i understand the issue is that i try to fit to much columsn into the dummy variable. But how do i fix it? Im not so long working with SQL so any help would be appreciated.
That is not a useful or simple way to test queries.
It’s a lot of code so it’s not particularly easy, and uses a cursor to process the results so it includes the cost of processing the results on the server with a cursor, which is not present normally.
Normally you just run the query in SSMS and look at the Actual Execution Plan and perhaps the time and IO statistics, and perhaps the client statistics. The query results should be returned to the client, because that's what happens in production, and you should consider the time needed to transmit results over the network when benchmarking.
If you need to run a query without returning data to the client, you can use a pattern like
go
set statistics io on
set statistics time on
go
drop table if exists #foo;
with q as
(
select ...
from ...
)
select *
into #foo
from q
go 5
set statistics io off
set statistics time off
go

frequently DELETE in Stored Procedure

How I can create a stored procedure and use frequently query like this:
SET NOCOUNT ON;
DECLARE #r INT;
SET #r = 1;
WHILE #r > 0
BEGIN
BEGIN TRANSACTION;
DELETE TOP (100000)
dbo.table1
WHERE Create_Date < DATEADD(YEAR, -5, GETDATE());
SET #r = ##ROWCOUNT;
COMMIT TRANSACTION;
CHECKPOINT;
END
in my new stored procedure?
Thanks for Your answers.
You can make your DELETE statements dynamic using something like below:
CREATE PROCEDURE dbo.DeleteRows (
#tableName VARCHAR(50),
#timestampColName VARCHAR(100),
#since DATETIME2,
#rows INT = 100000
AS
BEGIN
SET NOCOUNT ON;
DECLARE #r INT;
SET #r = 1;
WHILE #r > 0
BEGIN
-- SQL injection might be a problem if table and column name are not coming from a trustworthy source (i.e. user input)
DECLARE #SQL = N'
DELETE TOP (' + CAST(#Count AS INT) + ')' + #tableName + '
WHERE ' + #timestampColName + ' < #since;'
EXEC sp_executesql #SQL, N'#since DATETIME', #since = #since
SET #r = ##ROWCOUNT;
END
END
SQL injection can be tackled using one of the techniques indicated in this question and its answers.

Incrementing a parameter of type INT in dynamic SQL

I'm trying to increment a parameter set within my script by 1 every loop of my while.
This is an example of what I'm doing within my script:
DECLARE #I AS INT;
SET #I = 0;
DECLARE #SQL NVARCHAR(MAX)
SET #SQL =
'WHILE '+ Convert(Varchar, #I) +' < (SELECT statement here...)
BEGIN
SET '+ Convert(Varchar, #I) +' = '+ Convert(Varchar, (#I + 1))'
END'
There is a lot more to this script but this is the relevant part. I understand that '+ Convert(Varchar, #I) +' is just going to concatenate the value of #I to the string, but could anyone offer any advice in how I can make it so that the value of #I is incremented by 1.
Currently when executing the sql, the set command will end up like the following:
SET 0 = 0 + 1
where as I need it to change the actual variables value for the next loop.
Is this even possible?
You can use the '#I' as a variable in your query:
I'm trying to increment a parameter set within my script by 1 every loop of my while.
This is an example of what I'm doing within my script:
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = '
DECLARE #I AS INT;
SET #I = 0;
WHILE #I +' < (SELECT statement here...) +
'
BEGIN
SET #I = #I + 1
END'

sp_OAMethod 'DeleteFile' Method not working in SQL Server 2008 R2

I have an sql stored procedure that I use to delete files from the windows file system using the sp_OAMethod. This used to work fine when we were using sql server 2005, however, it does not work at all now when using sql server 2008 R2. I have read that you can use SQLDMO/SQLCLR however, I cannot find any decent information regarding these methods. My previous code is below:
-- declare variables
declare #ObjectID nvarchar(10),
#ObjectType nvarchar(255),
#BackupName nvarchar(255),
#BackupLocation nvarchar(255),
#ExpiryDate datetime,
#DeletedStatus bit,
#SQL nvarchar(4000),
#SQL1 nvarchar(4000),
#SQL2 nvarchar(4000),
#Result int,
#FSO_Token int,
#FileLocation nvarchar(4000)
-- declare cursor for table backups
declare backupexpired_cursor cursor for
select dbo.tbl_BackupObjects.ObjectID, dbo.tbl_BackupObjects.ObjectType, dbo.tbl_BackupObjects.BackupName,
dbo.tbl_BackupObjects.BackupLocation, dbo.tbl_BackupObjects.ExpiryDate, dbo.tbl_BackupObjects.Deleted
from dbo.tbl_BackupObjects
where dbo.tbl_BackupObjects.Deleted <> 1
-- open cursor
open backupexpired_cursor
-- fetch the next record from the cursor
fetch next from backupexpired_cursor into #ObjectID, #ObjectType, #BackupName, #BackupLocation, #ExpiryDate, #DeletedStatus
while (##FETCH_STATUS <> -1)
begin
if (##FETCH_STATUS <> -2)
begin
if (#ExpiryDate < GetDate())
begin
if (#ObjectType = 'Table')
begin
begin try
begin transaction
-- Only done if the object type is a table object
-- Remove old backup
select #SQL = 'drop table dbo.' + quotename(#BackupName)
exec sp_executesql #SQL
-- update the deleted status and the date deleted of the deleted object
select #SQL1 = 'update tbl_BackupObjects
set Deleted = 1,
DeletedDate = GetDate()
where ObjectID = ''' + #ObjectID + ''''
exec sp_executesql #SQL1
commit transaction
end try
begin catch
rollback transaction
select #SQL1 = 'update tbl_BackupObjects
set Deleted = 0,
DeletedDate = NULL
where ObjectID = ''' + #ObjectID + ''''
exec sp_executesql #SQL1
end catch
end
else
begin
begin try
begin transaction
-- Only done if the object(view, stored procedure, and/or function is saved
-- in a file located on the windows file system.
-- Create File Location
set #FileLocation = 'G:\Backup Registry Script Files\' + #BackupLocation + '\' + #BackupName + ''
-- Create a token of the object
EXEC #Result = sp_OACreate 'Scripting.FileSystemObject', #FSO_Token OUTPUT
-- Call the deletefile method using the #FileLocation parameter and the token created above:
-- - The object token created by sp_OACreate
-- - The method name
-- - The method's return value
-- - Parameters that will be used by the object method
EXEC #Result = sp_OAMethod #FSO_Token, 'DeleteFile', NULL, #FileLocation
-- Execute ole method
EXEC #Result = sp_OADestroy #FSO_Token
-- update the deleted status and the date deleted of the deleted object
select #SQL1 = 'update tbl_BackupObjects
set Deleted = 1,
DeletedDate = GetDate()
where ObjectID = ''' + #ObjectID + ''''
exec sp_executesql #SQL1
commit transaction
end try
begin catch
rollback transaction
select #SQL1 = 'update tbl_BackupObjects
set Deleted = 0,
DeletedDate = GetDate()
where ObjectID = ''' + #ObjectID + ''''
exec sp_executesql #SQL1
end catch
end
end
end
-- fetch the next record from the cursor
fetch next from backupexpired_cursor into #ObjectID, #ObjectType, #BackupName, #BackupLocation, #ExpiryDate, #DeletedStatus
end
-- set the Last and Next Removal Dates
select #SQL2 = 'update tbl_BackupRemovalDate
set LastRemovalDate = GetDate(),
NextRemovalDate = GetDate() + 7'
exec sp_executesql #SQL2
-- close cursor
close backupexpired_cursor
deallocate backupexpired_cursor
I have seen that SQLDMO is quite similar to what I have, however I cannot find any information on how to delete a file system file using this method. Can anyone help?
Do you have Enable Ole Automation Procedures feature ?
try this
EXEC sp_configure 'show advanced options', 1
RECONFIGURE
EXEC sp_configure 'Ole Automation Procedures', 1
RECONFIGURE

Exporting binary file data (images) from SQL via a stored procedure

I am trying to export a fairly large number of image files, stored internally in an SQL database as binary data.
Being fairly new to writing stored procedures in SQL, I have come across a couple of very useful guides on how this can be archived, but I seem to be missing something.
I am running SQL Server 2008 R2 locally, and I am trying to write the files to a folder on my C:\ drive.
Here is the buisness part of what I have so far:
BEGIN
DECLARE #cmd VARCHAR(8000)
DECLARE #result int
DECLARE curExportBinaryDocs CURSOR FAST_FORWARD FOR
SELECT 'BCP "SELECT Photograph_Data FROM [ALBSCH Trial].[dbo].[Photograph] WHERE Photograph_ID = '
+ CAST(Photograph_ID AS VARCHAR(500)) + '" queryout "' + #OutputFilePath
+ CAST(Photograph_ID AS VARCHAR(500)) + '.jpg"' + ' -n -T'
FROM dbo.Photograph
OPEN curExportBinaryDocs
FETCH NEXT FROM curExportBinaryDocs INTO #cmd
WHILE ##FETCH_STATUS = 0
BEGIN
--PRINT #cmd
EXEC #result = xp_cmdshell #cmd
FETCH NEXT FROM curExportBinaryDocs INTO #cmd
END
CLOSE curExportBinaryDocs
DEALLOCATE curExportBinaryDocs
END
'#result' is always being set to '1' (failed) after the xp_cmdshell call. All the table names/fields are correct, so I suspect there is something wrong with my BCP call, but I am not sure what to try next.
Any help or advice would be very welcome.
Well, first of all.. (and sorry about that ;) ) DON"T USE CURSORS..
and sorry for the caps...
One of the most baddest things about cursors are that they can lock your table. What i always do for these purposes (and which is quite faster), i use a for loop.. like this
declare #totrow int
, #currow int
, #result int
, #nsql nvarchar(max)
declare #sqlStatements table (
Id int identity(1, 1)
, SqlStatement varchar(max)
)
insert
into #sqlStatements
select 'QUERY PART'
from table
set #totrow = ##rowcount
set #currow = 1
while #totrow > 0 and #currow <= #totrow
begin
select #nsql = SqlStatement
from #SqlStatements
where Id = #currow
exec #result = xp_cmdshell #nsql
set #currow = #currow + 1
end
For the next part, does the SQL Server process has enough permission to write to the c: drive? Also, look into your message pane when you execute your code, maybe you can find something there?
What you also can do, try to execute it manually. Just get one BCP statement and execute it with the xp_cmdshell. Does it gives any errors?
Here is my final working procedure and format file. I was not able to find the finer details of
BCP commands, permision settings and format file layouts in one place, so maybe this will be of use to someone.
CREATE PROCEDURE [dbo].[ImgExport]
#OutputFilePath VARCHAR(500) = 'C:\SQLTest\ '
AS
BEGIN
DECLARE #totrow int
DECLARE #currow int
DECLARE #result int
DECLARE #nsql nvarchar(4000)
DECLARE #sqlStatements table (ID int IDENTITY(1, 1), SqlStatement varchar(max))
INSERT
INTO #sqlStatements
SELECT 'BCP "SELECT Photograph_Data FROM [ALBSCH_Trial].[dbo].[Photograph] WHERE Photograph_ID = '''
+ CAST(Photograph_ID AS VARCHAR(500)) + '''" queryout ' + #OutputFilePath
+ CAST(Photograph_ID AS VARCHAR(500)) + '.jpg -S localhost\SQLEXPRESS2008 -T -f C:\SQLTest\Images.fmt'
FROM dbo.Photograph
SET #totrow = ##ROWCOUNT
SET #currow = 1
WHILE #totrow > 0 and #currow <= #totrow
BEGIN
SELECT #nsql = SqlStatement
FROM #sqlStatements
WHERE ID = #currow
EXEC #result = xp_cmdshell #nsql
SET #currow = #currow + 1
END
END
Format file:
9.0
1
1 SQLBINARY 0 0 "\t" 1 Photograph_Data ""
I hope that helps somebody.