SQL Server Syntax failure - sql-server

I am running the following code and I am getting a syntax error near '.TableReference' error, the code use to work then I did something and now I have this error and I can't seem to find the issue
Through troubleshooting I have narrowed the code issue to the ' FROM ' + #TableName section but it appears to be good code.
BEGIN
--SET NOCOUNT ON;
DECLARE #TableName AS NVARCHAR(MAX) --The Fully qualified database name
DECLARE #Ref AS NVARCHAR(MAX) --The name of the Table we are processing
DECLARE #TempTab AS NVARCHAR(MAX) --the temporary table we are subjecting to the tortures of this process
DECLARE #TempQuery AS NVARCHAR(MAX) --Query to move all data into the temporary table
--This selects the first record in the Website Request Table which hasn't been processed and passes it into the TempTab variable
SET #NDTRef = (SELECT TOP 1 Reference from dbo.WebRequestTable Where Processing IS NULL)
SET #TableName = 'Processing.dbo.'+#NDTRef
Set #TempTab = 'TEMP' + #NDTRef
SET #TempQuery = 'SELECT * INTO '+ #TempTab +' FROM ' + #TableName
EXEC sp_sqlexec #TempQuery;
END
Any help would be appreciated it is a stand alone instance of SQL Server 2019 and the code is a part of a stored procedure but the rest of the code runs off the temporary table created in this block
After suggestions I put in a print statement regarding the #TempQuery when put straight after and the EXEC removed the output is
SELECT * INTO TEMP2294690 FROM Processing.dbo.2294690
With the EXEC back in play I get the error
Msg 102, Level 15, State 1, Line 17 Incorrect syntax near '.2294690'.
The print output after the EXEC shows:
SELECT * INTO TEMP2294690 FROM Processing.dbo.2294690
The Table 2294690 exists in the database Processing the Temp2294690 is a table that should be created by this block but it isn't being created

In SQL Server, regular Identifiers must begin with a letter, an underscore (_), at sign (#) or the number sigh (#).
(There are other rules as well, but this is the one relevant to the question...)
Identifiers that don't follow the rules of regular identifiers can be only used if they are enclosed in square brackets ([]) or double quotation marks (").
The best way to handle identifiers when creating dynamic SQL statements is to use the built in QUOTENAME function - this way you can make sure your query doesn't break even if the identifier doesn't follow the rules of regular identifiers.
So your SQL should look like this:
SET #TableName = '[Processing].[dbo].'+ QUOTENAME(#NDTRef)
SET #TempTab = 'TEMP' + #NDTRef
SET #TempQuery = 'SELECT * INTO '+ QUOTENAME(#TempTab) +' FROM ' + #TableName
That being said, you should also probably check my blog post entitled The do’s and don’ts of dynamic SQL for SQL Server where you can find some more information about how to safely create dynamic SQL.

You know that if the view is temporary only you can see it and if you close your session user the view is deleted and dont save try with create view dont temp and drop after you read.
If all previus you have it in your mind you could try this for see if the view has created --> its a extract of Microsoft official pagge:
VIEW_METADATA
Specifying the instance of SQL Server will return the DB-Library, ODBC, and OLE DB APIs the metadata information about the view instead of the base tables when you request the browse mode metadata for a query that references the view. Browse mode metadata is additional metadata that the SQL Server instance returns to these client-side APIs. This metadata enables client-side APIs to implement updateable client-side cursors. Browse mode metadata includes information about the base table to which the columns in the result set belong.

Related

Adding a new column to a table getting the column name from another table

I'm using Microsoft SQL server management studio.
I would like to add a new column to a table (altertable1), and name that column using the data from a cell (Date) of another table (stattable1).
DECLARE #Data nvarchar(20)
SELECT #Data = Date
FROM stattable1
WHERE Adat=1
DECLARE #sql nvarchar(1000)
SET #sql = 'ALTER TABLE altertable1 ADD ' + #Data + ' nvarchar(20)'
EXEC (#sql)
Executing this, I get the following error and can't find out why:
"Incorrect syntax near '2021'."
The stattable1 looks like this:
Date |Adat
2021-09-08 |1
2021-09-08 is a daily generated data:
**CONVERT(date,GETDATE())**
Just like Larnu said in comment, maybe this is not a main problem for you, but if you want to do this add [ ] when you want to name column starting with number.
Like this:
SET #sql = 'ALTER TABLE altertable1 ADD [' + #Data + '] nvarchar(20)'
And of course, naming columns by date or year is not best practice.
The problem with your overall design is that you seem to be adding a column to the table every day. A table is not a spreadsheet and you should be storing data for each day in a row, not in a separate column. If your reports need to look that way, there are many ways to pivot the data so that you can handle that at presentation time without creating impossible-to-maintain technical debt in your database.
The problem with your current code is that 2021-06-08 is not a valid column name, both because it starts with a number, and because it contains dashes. Even if you use a more language-friendly form like YYYYMMDD (see this article to see what I mean), it still starts with a number.
The best solution to the local problem is to not name columns that way. If you must, the proper way to escape it is to use QUOTENAME() (and not just manually slap [ and ] on either side):
DECLARE #Data nvarchar(20), #sql nvarchar(max);
SELECT #Data = Date
FROM dbo.stattable1
WHERE Adat = 1;
SET #sql = N'ALTER TABLE altertable1
ADD ' + QUOTENAME(#Data) + N' nvarchar(20);';
PRINT #sql;
--EXEC sys.sp_executesql #sql;
This also demonstrates your ability to debug a statement instead of trying to decipher the error message that came from a string you can't inspect.
Some other points to consider:
if you're declaring a string as nvarchar, and especially when dealing with SQL Server metadata, always use the N prefix on any literals you define.
always reference user tables with two-part names.
always end statements with statement terminators.
generally prefer sys.sp_executesql over EXEC().
some advice on dynamic SQL:
Protecting Yourself from SQL Injection - Part 1
Protecting Yourself from SQL Injection - Part 2

Pass Linked Server name as parameter to Stored Procedure (not using Dynamic TSQL)

I need to pass linked server name as variable to stored procedure right now after testing and research they all suggest to using dynamic sql and open query which I am using now. however I am not comfortable using it(sql injection) plus I need to call other user defined function to the query. I am looking for a more secure and direct call. Here is my SP
ALTER PROCEDURE [dbo].[GetBackUpStatus]
-- Add the parameters for the stored procedure here
#linkedServerName AS VARCHAR(100),
#exemptDB as VARCHAR(100)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
DECLARE #Sql varchar(8000)
SET NOCOUNT ON;
set #Sql = 'select * from openquery (' + #linkedServerName + ' , ''SELECT SERVERPROPERTY(''''SERVERNAME'''') AS "SERVERNAME",
T1.NAME AS DatabaseName,
MAX(T2.backup_finish_date) AS last_db_backup_date,
CAST(COALESCE(DATEDIFF(second, MAX(T2.backup_finish_date) , CURRENT_TIMESTAMP)/ 3600.0, 9999.0) as NUMERIC(6,2)) AS [Hours Since Backup]
FROM master.sys.databases T1
LEFT OUTER JOIN msdb.dbo.backupset T2 ON T2.database_name = T1.NAME
WHERE T1.NAME NOT IN (''''tempdb'''')
GROUP BY T1.NAME
ORDER BY T1.NAME'')'
Exec (#Sql)
END
the purpose of this query is to get the server status and its database, I don't like this because of that confusing single quotes, this query will eventually grow as I develop and add function calls.
I tried this and something like this is what I wanted, since it is direct query and cleaner without those quotes. That's how I typically use linked server.
Select * from [' + #linkedservername + '].[schema].table
thanks
Solution for a large scale data model with hundreds of tables / objects.
Dynamic modification and cloning of a stored procedure for every linked-server.
It is kinda hiding any dynamic SQL under the hood.
How to
Create a stored procedure which interacts with an existing linked-server.
During a database deployment process:
Obtain the source code of the stored procedure.
Replace the name of the linked-server in the code.
If you want to create a new stored procedure (cloned), replace the name of the initial stored procedure in the code.
Create a cloned stored procedure or modify the current.
Repeat all steps for each required linked-server.
There are another variations for it.
Now, any external logic may decide which procedure to use.
You can check the existence of a linked-server or its related stored procedure.
For modifications and cloning, it is possible to use SQL Server or external tools, such as C#, CMD, etc.
For creation under SQL Server.

How to create "dynamic" stored proc without using dynamic SQL

I have an existing SP.
I want to start using database snapshots in some cases.
Users can save a snapshot at any time.
If they do, the SP should use it.
Otherwise, use the primary database.
I want to adapt the SPs to handle this, by making the database/table names dynamic instead of hard-coded in the SP.
I can imagine something like this working, with fully qualified table names, but it gives the error 'Must declare the table variable "#db1"':
declare #table1 varchar(25);
set #table1 = Snapshot.schema.tablename;
select * from #table1;
This gives "Incorrect syntax near '#db'."
declare #db varchar(25);
set #db = "Snapshot";
use #db;
This sorta works, but the "use" is only in effect during the execute. By the time the 'select' is executed, you are back to using the original database.
declare #db varchar(25);
set #db = 'use Snapshot';
EXECUTE(#db);
select * from Schema.Tablename;
I know I could generate the entire SP on the fly, but that seems guaranteed to get rejected by the DBAs. I'd rather a less radical solution.
Any better ideas?

Multiple websites that uses same database structure

I have three websites which uses an abstract database structure with tables like: Items, Places, Categories, etc... and stored procedures like GetItemsByCategory, GetRelatedItems, etc... Actually im using exactly the same database structure for these 3 different websites.
From a code perspective im using the same code for all websites (except the HTML which is specific foreach one), and all the common code is in few projects used by all websites, so everytime that i detect a bug (which is in all websites) i just fix it on one place (the common part used by all) and automatically all websites get the fix.
Actually im using Asp.net MVC3 and Sql server.
Everytime i want to extend some funcionality, and i need a new table, stored procedure or something related with database, i have to do the modification in each database.
Do you know any approach that i could use to be able to have the same flexibility and do database modifications only one time for all websites?
Do you think I'm using a good approach or i should use something different in your opinion?
If the databases are on a single server, you could generate the script for the procedure from Management Studio, and make sure to use the option to "check for object existence" (Tools > Options > SQL Server Object Explorer > Scripting). This will yield something like this (most importantly it produces your stored procedure code as something you can execute using dynamic SQL):
USE DBName;
GO
SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
IF NOT EXISTS (...)
BEGIN
EXEC dbo.sp_executesql #statement = N'CREATE PROCEDURE dbo.whatever ...
'
END
GO
Now that you have this script, you can modify it to work across multiple databases - you just need to swipe the #statement = portion and re-use it. First you need to stuff the databases where you want this to work into a #table variable (or you can put this in a permanent table, if you want). Then you can build a command to execute in each database, e.g.
DECLARE #dbs TABLE (name SYSNAME);
INSERT #dbs(name) SELECT N'db1';
INSERT #dbs(name) SELECT N'db2';
INSERT #dbs(name) SELECT N'db3';
-- now here is where we re-use the create / alter procedure command from above:
DECLARE #statement NVARCHAR(MAX) = N'CREATE PROCEDURE dbo.whatever ...
';
-- now let's build some dynamic SQL and run it!
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + '
EXEC ' + QUOTENAME(name) + '.dbo.sp_executesql N''' + #statement + ''';'
FROM #dbs;
EXEC sys.sp_executesql #sql;
Alternatively, you could create a custom version of my sp_msforeachdb or sp_ineachdb replacements:
Making a more reliable and flexible sp_MSforeachdb
Execute a Command in the Context of Each Database in SQL Server
I used to use a tool called SQLFarms Combine for this, but the tool doesn't seem to exist anymore, or perhaps it has been swallowed up / re-branded by another company. Red Gate has since produced SQL Multi Script that has similar functionality.
If you added a column to all your tables called websiteId you could just have one database. Store the unique websiteId in each site's web.config and just pass it with each request for data. Obviously each site's data is stored with their websiteId so data can be queried per website.
It means a bit of refactoring in your db and any calls to your your db, but once done, you only have one database to maintain.
Of course this is assuming your databases are on the same server...

Access SQL Server temporary tables created in different scope

I am writing a stored procedure for SQL Server 2008 in which I need to extract information from a set of tables. I do not know ahead of time the structure of those tables. There is another table in the same database that tells me the names and types of the fields in this table.
I am doing this:
declare #sql nvarchar(max)
set #sql = 'select ... into #new_temporary_table ...'
exec sp_executesql #sql
Then I iterate doing:
set #sql = 'insert into #another_temporary_table ... select ... from #new_temporary_table'
exec sp_executesql #sql
After that I drop the temporary table. This happens in a loop, so the table with be created, populated and dropped many times, each time with different columns.
This fails with the error:
Invalid object name: #new_temporary_table.
After some googling I have found that:
The table #new_temporary_table is being created in the scope of the call to exec sp_executesql which is different from the one of my stored proc. This is the reason the next exec sp_executesql cannot find the table. This post explains it:
http://social.msdn.microsoft.com/forums/en-US/transactsql/thread/1dd6a408-4ac5-4193-9284-4fee8880d18a
I could use global temporary tables, which are prepended with ##. I can't do this because multiple stored procs could run at the same time and they would be affecting each other's state
In this article it says that if I find myself in this situation I should change the structure of the database. This is not an option for me:
http://www.sommarskog.se/dynamic_sql.html
One workaround I have found was combining all the select into #new_temporary_table.. and all the insert into ... scripts into one gigantic statement. This works fine but it has some downsides.
If I do print #sql to troubleshoot, the text gets truncated, for example.
Do I have any other option? All ideas are welcome.
You could use global temp tables, but use a context id (such as newid()) as part of the global temp table name.
declare #sql varchar(2000)
declare #contextid varchar(50) = convert(varchar(20), convert(bigint, substring(convert(binary(16), newid()), 1, 4)))
set #sql = 'select getdate() as stuff into ##new_temporary_table_' + #contextid
exec (#sql)
I think it's best to use one single script.
You can change how many characters will print in Tools > Options > Query Results > SQL Server > Results to Text - change "Maximum number of characters..." from 256 to the max (8192).
If it's bigger than 8192, then yes, printing is difficult. But you could try a different option in this case. Instead of PRINT #sql; instead use the following (with Results to Grid):
SELECT sql FROM (SELECT #sql) AS x(sql) FOR XML PATH;
Now you can click on the result, and it opens in a new query window. Well, it's an XML file window, and you can't execute it or see color-coding, and you have to ignore that it changes e.g. > to > to make it valid as XML data, but from here it's easy to eyeball if you're just trying to eyeball it. You can copy and paste it to a real query editor window and do a search and replace for the entitized characters if you like. FWIW I asked for them to make such XML windows real query windows, but this was denied:
http://connect.microsoft.com/SQLServer/feedback/details/425990/ssms-allow-same-semantics-for-xml-docs-as-query-windows
#temp tables (not global) are available in the scope they were created and below.
So you could do something like...
while (your_condition = 1) begin
set #sql = 'select ... into #temp1 ...from blah
exec sp_do_the_inserts'
exec(#sql)
end
The sp_do_the_inserts might look like...
select * into #temp2 from #temp1
....your special logic here....
This assumes you create sp_do_the_inserts beforehand, of course.
Don't know if that serves your need.

Resources