SQL column value to table name - sql

In my SQL database, I would like to make a general purpose soft link table. Perhaps something similar the following:
create table soft_link(
id uniqueidentifier,
name varchar(255),
LINK uniqueidentifier,
TABLE varchar(255),
primary key(id));
Say I have object "b_object" in some other table in the database. The LINK column would be the the unique identifier of b_object, and TABLE would be the table in which b_object is stored within the database.
Now I want to make a single stored procedure "sp_ResolveSoftLink". This method will take an id of a soft link, look up the LINK and TABLE columns of the soft link, and then use the TABLE and LINK to query for b_object.
The following is NOT proper SQL syntax, but hopefully it helps illustrate my question. I want to combine these two queries into a single stored procedure and return the result of the second query:
select LINK, TABLE from soft_link where id = xxxxxx
select * from TABLE where id = LINK
-- return the result of the second select query
FUNDAMENTAL QUESTION: How/can I use the varchar return from one query to form another query in the same stored procedure in SQL?

You can create a Stored procedure like this.
Here, First you have to store value of LINK and Table in variable and then use these variable to form another Dynamic Query
CREATE PROC spName
#id INT
AS
BEGIN
Declare #LINKValue uniqueidentifier, #TABLE varchar(255),#SQL varchar(max)
SELECT #LINKValue = LINK , #TABLE = TABLE
FROM soft_link
WHERE soft_link id = #id
SET #SQL='SELECT * FROM';
SET #SQL = #SQL +' ' + #TABLE
SET #SQL = #SQL +' ' + 'WHERE ID =' +' ' + #LINKValue
PRINT #SQL -- SEE Here This is what you actually Want
--EXEC (#SQL) --Then Execute this
END

Related

How to use a variable in "Select [some calculations] insert into #NameOfTheTableInThisVariable"?

I have a procedure in which there are calculations being done and the final result is inserted into a permanent table. I want to remove the permanent table and I cannot use Temp table as well. So i want to use a dynamic table name, which is stored in a variable:
Current scenario:
Insert into xyz_table
Select col1,col2,sum(col3)
from BaseTable
(In reality, there are lot of columns and a lot of calculations)
What I want:
Select col1,col2,sum(col3) into #DynamicTableName
from BaseTable
where the name of the table would be dynamic in nature i.e.,
#DynamicTableName = 'xyz ' + cast(convert(date,getdate()) as nvarchar)+' '+convert(nvarchar(5),getdate(),108)
It will have date and time in its name every time the procedure is run.
I want to use this name in the "Select * into statement"
How can I achieve this?
i tried it with the some short code. But since my procedure has a lot of calculations and UNIONS , I cannot use that code for this. Any help would be appreciated.
declare #tablename nvarchar(30)= 'xyz ' + cast(convert(date,getdate()) as nvarchar)+' '+convert(nvarchar(5),getdate(),108)
declare #SQL_Statement nvarchar(100)
declare #SQL_Statement2 nvarchar(100)
declare #dropstatement nvarchar(100)
SET #SQL_Statement = N'SELECT * Into ' +'['+#tablename +'] '+'FROM '+ 'dimBranch'
print #SQL_Statement
EXECUTE sp_executesql #SQL_Statement
SET #SQL_Statement= N'select * from ' + '['+#tablename + '] '
print #SQL_Statement
EXECUTE sp_executesql #SQL_Statement
set #dropstatement = 'DROP TABLE' + '['+#tablename + '] '
PRINT #dropstatement
exec sp_executesql #dropstatement
Reason why I want this is because I use this procedure in ETL job as well as in SSRS report. And if someone runs the package and the SSRS report at the same time, the incorrect or weird data gets stored in the table. Therefore I need a dynamic name of the table with date and time.
You can't parameterize an identifier in SQL, only a value
--yes
select * from table where column = #value
--no
select * from #tablename where #columnname = #value
The only thin you can do to make these things dynamic is to build an sql string and execute it dynamically, but your code is already doing this with sp_executesql
More telling is your complaint at the bottom of your question, that if the procedure is invoked simultaneously it gives problems. Perhaps you should consider using local table variables for temporary data storage that the report is using rather than pushing data back into the db
DECLARE #temp TABLE(id INT, name varchar100);
INSERT INTO #temp SELECT personid, firstname FROM person;
-- work with temp data
select count(*) from #temp;
--when #temp goes out of scope it is lost,
--no other procedure invoked simultaneously can access this procedure'a #temp
Consider a local temp table, which is automatically session scoped without the need for dynamic SQL. For example:
SELECT *
INTO #YourTempTable
FROM dimBranch;
The local temp table will automatically be dropped when the proc completes so there is no need for an explict drop in the proc code.

How to use variables when using more than one "use db"

I have a SQL script that I've been working on making more flexible. I need it to be able to insert rows into tables in one DB then create a table and insert rows in another DB on the same server. I can each of these things in two different scripts of course, but I'm trying to figure out how to do it all in one. Here is an example of what I've tried:
use firmTriad
create table #TempTable (IssueId nvarchar(300),[SQL] nvarchar(300),FirmID varchar(255),[Table] varchar(255),SecurityDelete varchar(255)
,SecurityKeep varchar(255))
declare #IssueID nvarchar(300),#SQL nvarchar(300),#FirmID varchar(255),#SecurityCUSIPdelete varchar(255),#SecurityCUSIPkeep varchar(255)
set #SecurityCUSIPkeep = 'AET133A20'
set #SecurityCUSIPdelete = 'AEXVA0559'
set #FirmID = 'firmTriad'
set #IssueID = 'RQST00002652426'
set #IssueID = 'tblSecurity_MSReference_' + #IssueID
insert into #TempTable (IssueId,FirmID,SecurityDelete,SecurityKeep)
select #IssueID,#FirmID,#SecurityCUSIPdelete,#SecurityCUSIPkeep
use PSBackup
select * from #TempTable
declare #Table varchar(255), #PSTable varchar(255)
set #PSTable = 'tblSecurity_MSReference_' + #IssueID
set #Table = #FirmID + '.dbo.' + left(#Table,len(#Table)-16)
print (#FirmID)
set #SQL = 'create table ' + #IssueID + '([RecID],[SecurityID],[MSCUSIP],[LastUpdateDT]
,Issue_Key,Issue_Backup_Date,Issue_Backup_Len)'
exec #SQL
insert into sys. (#firmID + '.dbo.tblSecurity_MSReference') --firmTriad.dbo.tblSecurity_MSReference
select [RecID],[SecurityID],[MSCUSIP],[LastUpdateDT],Issue_Key,Issue_Backup_Date,Issue_Backup_Len
from #Table
where [SecurityID] in(select RecID from firmTriad.dbo.tblSecurity where CUSIP in(#SecurityCUSIPdelete,#SecurityCUSIPkeep))
I can't figure out how to get the variables to carry over between the two use db sections.
I see you are creating a materialized temp table (#TempTable) and then doing some SELECT queries and then creating a new table and inserting some data there.
As I understand, your problem is with cross-database variables, right?
Does it help you to know, that this works:
create table PSBackup.dbo.TheNameOfYourTable(
IssueId nvarchar(300),
[SQL] nvarchar(300),
FirmID varchar(255),
[Table] varchar(255),
SecurityDelete varchar(255),
SecurityKeep varchar(255)
);
You do not need to "use PSBackup" in order to create a table or basically do anything in that database. The generic "formula" is:
SELECT * FROM <dbname>.<schemaname>.<tablename>
EXEC <dbname>.<schemaname>.<StoredProcedureName>
The statement "USE PSBackup" changes the context in which you are working in. If you are creating a table and dont provide dbname and schemaname, the SQL server just uses the defaults (which are master and dbo). And of course, if you change the context, the variables are discarded. Think in terms of transactions and it will become clear as day.
The other, non-related thing is, you really shouldn't use reserved names for column names. This can cause all sorts of problems in the future...Try changing the column names:
"[SQL]" to "Query"
"[Table]" to "TBL" or "TableName"
Does this help?

Dynamically joining tables that may not exist

What's a good way to dynamically create tables, then join together all the tables that I have created?
Background
I'm trying to write a stored procedure that collects data from various parts of the database and presents it for use. For example if I wanted to send invoices to both Alice and Bob, this stored procedure would find all the information for the invoice. The trick is that Alice and Bob don't always need all the information I have. If Bob is tax exempt but trades in simoleons, I'll need to include information about currency exchange rates but leave out tax info. I want to give the stored procedure an order number and get back results formatted to the customer.
My solution so far is to create a table holding customer requirements. Then my stored procedure checks the table: if currency data is required I create and populate #Currency, or #Taxes holds tax data and so on. I check the requirement, then create the table as needed. At the end I need to Join the data into one record set.
My problem is that I have a error saying
Incorrect syntax near the keyword 'table'
AND an error saying
Must declare the table variable #Summary
on line 1 where i define #Summary These errors only show up when I use dynamic SQL to join the tables. When I comment out the dynamic SQL and copy-paste the statements into the one it should create, I have the result I want.
Code
I tried to use the following dynamic SQL in Microsoft SQL Server Management Studio
create procedure <procedure> (#OrderNumber varchar(20)) AS
DECLARE #CustomerName varchar(35) -- retrieved based on #OrderNumber
DECLARE #SQLQuery nvarchar(500)
DECLARE #ParamDef nvarchar(500)
DECLARE #SummaryTable table
(
ID varchar(20) --=#OrderNumber
, <Stuff>
)
SET #SQLQuery = 'Select * From #SummaryTable'
SET #ParamDef = '#SummaryTable table'
IF EXISTS(Select <TableName> from CustRequirements where <TableName> = 1 AND Customer = #CustomerName)
BEGIN
--Create table
DECLARE #<TableName> table
(
ID varchar(20)
, <Stuff>
)
--Populate
Insert into <TableName> Select <RelevantData> From <DataSource> where <Condition based on #OrderNumber or #CustomerName>
--Pepare Dynamic SQL
Set #SQLQuery = #SQLQuery + ' FULL OUTER JOIN #<TableName> ON <TableName>.ID = SummaryTable.ID'
SET #ParamDef = ', #<TableName> table'
END
<repeat for other tables>
EXECUTE sp_executesql #SQLQuery, #ParamDef, #Summary, #LineItems, #Taxes, #Currency
Question
Is there something wrong with my code? Is there a better way to do this? The other option I though of was to have IF Exists trees with the entire JOIN statement at the bottom of each branch (Since I can't seem to interrupt the JOIN clause with IF's). The problem with that is that I'll need 2^n JOIN statements to join n possible tables. n in this case might be as high has 20 or 30.
I think I see a problem in the code (maybe 3 problems -- see "questionable" 1 & 2 below) --
1) [Changed on 10/21, after OP's comments] The big problem: table parameters passed in the final "EXECUTE sp_executesql #SQLQuery..." are sometimes not declared.
1a) #Summary is actually never declared... you declared and set #SummaryTable, then use #Summary.
Just change that to #SummaryTable (I did that in (4) below), and I think that will prevent your second error message ("Must declare the table variable #Summary").
1b) All the other tables are sometimes declared: each of their DECLARE statements are within an "IF EXISTS". I suggest either (I) make the declares unconditional (outide IF EXISTS), but still conditionally INSERT... or (II) make the format of the EXECUTE command vary with what's available.
(I doubt the unused table variables need anything in them....)
In point 4 below (added 10/21), I give an example that I have NOT TESTED with my own databases (that test would take a lot more time)... so please let me know how it goes...
2) [questionable 1] Simple case of mixed case ;) Note that the line...
Set #SQLQuery = #SqlQuery + ' FULL OUTER JOIN #<TableName> ON <TableName>.ID = SummaryTable.ID'
... first has an uppercase "SQL", then mixed-case "Sql".
Why I said this is "questionable" -- if your server's COLLATION is case-insensitive, what you typed above would be fine.
3) [questionable 2] You have both '#TableName' and '#Table Name' (with a space). I realize that may just be how you typed it, in posting your question.
Point (4), added in update to answer -- possible code
create procedure <procedure> (#OrderNumber varchar(20)) AS
DECLARE #CustomerName varchar(35) -- retrieved based on #OrderNumber
DECLARE #SQLQuery nvarchar(500)
DECLARE #ParamDef nvarchar(500)
DECLARE #SummaryTable table
(
ID varchar(20) --=#OrderNumber
, <Stuff>
)
SET #SQLQuery = 'Select * From #SummaryTable'
SET #ParamDef = '#SummaryTable table'
--Create table variables, though they may not be populated
DECLARE #LineItems
(
ID varchar(20)
, <Stuff>
)
DECLARE #Taxes
(
ID varchar(20)
, <Stuff>
)
DECLARE #Currencytable
(
ID varchar(20)
, <Stuff>
)
IF EXISTS(Select <TableName> from CustRequirements where <TableName> = 1 AND Customer = #CustomerName)
BEGIN
--Populate
Insert into <TableName> Select <RelevantData> From <DataSource> where <Condition based on #OrderNumber or #CustomerName>
--Prepare Dynamic SQL
Set #SQLQuery = #SQLQuery + ' FULL OUTER JOIN #<TableName> ON <TableName>.ID = SummaryTable.ID'
SET #ParamDef = ', #<TableName> table'
END
<repeat for other tables>
EXECUTE sp_executesql #SQLQuery, #ParamDef, #SummaryTable, #LineItems, #Taxes, #Currency
The solution I've decided tp use is to create a table then use ALTER TABLE to add columns as needed.
CREATE Table #Output
(
InvoiceNumber varchar(20)
, <stuff>
)
IF EXISTS(Select <ThisSegment> FROM CustRequirements WHERE <ThisSegment> = 1 AND Customer = #CustomerName)
BEGIN
ALTER TABLE #Output ADD <ThisSegment> <dataType>
UPDATE #Output
SET <ThisSegment> = <data> from <DataSource>
WHERE <InvoiceNumber = DataSource.invoice> AND <Other conditions>
END
<Repeat as needed>

Single SQL Query to update datatypes of all columns in a table at one shot

I have a SQL Server table which has 625 columns created with different datatypes like int, varchar(25), decimal(18, 2), etc...
Now am interested in changing datatype of all columns to varchar(255). Instead of executing below query for all the columns one by one, is there a single SQL Server query to change datatypes of all columns in a table at one shot?
ALTER TABLE dbo.Employee
ALTER COLUMN FirstName VARCHAR(255) NOT NULL
Looking forward for your response.
There is no one single "magic" bullet to do this - it's an operation that's rather unusual, so it's not supported natively.
What you can do is iterate over the columns of your table from the system catalog views, create such an ALTER statement on the fly, and also execute it - something like this:
DECLARE AlterTableCursor CURSOR FAST_FORWARD
FOR
SELECT
AlterCmd = 'ALTER TABLE dbo.YourTableNameHere ALTER COLUMN ' + name + ' VARCHAR(255) NULL'
FROM
sys.columns
WHERE
object_id = OBJECT_ID('dbo.YourTableNameHere')
DECLARE #AlterTableCmd NVARCHAR(200)
OPEN AlterTableCursor
FETCH NEXT FROM AlterTableCursor INTO #AlterTableCmd
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC (#AlterTableCmd)
FETCH NEXT FROM AlterTableCursor INTO #AlterTableCmd
END
CLOSE AlterTableCursor
DEALLOCATE AlterTableCursor
Replace the YourTableNameHere with your actual table name - and you should be good to go! Test this on a copy of your live data first !
is not possible. You will need to do this one by one. You can do following things -
You can create a Temporary Table with your desired columns and type.
copy the data from original table to temporary table.
delete original table
Rename you Temporary Table to your original name
I would say that it's not a good design to have 625 columns in one table, but you do what ever you have been asked to do.
You could generate a SQL string that contains multiple ALTER statements and execute it with EXEC. You have to be careful with the data conversion though. This is why it's probably better to do it in a cursor, because you can catch possible exceptions for the columns that cannot be altered. Here is a simple code which does it in two steps:
generate the SQL string;
execute it using EXEC
Code sample:
SELECT * FROM sys.columns WHERE OBJECT_NAME(object_id) = 'Employee'
DECLARE #SQL VARCHAR(MAX)
SET #SQL = (
SELECT '
ALTER TABLE dbo.Employee ALTER COLUMN ' + c.name + ' VARCHAR(255) NULL;
'
FROM sys.columns c WHERE OBJECT_NAME(object_id) = 'Employee'
FOR XML PATH(''), TYPE
).value('text()[1]', 'VARCHAR(MAX)')
PRINT #SQL
EXEC(#SQL)
SELECT * FROM sys.columns WHERE OBJECT_NAME(object_id) = 'Employee'

How to pass table name dynamically in T-SQL

I want to write sample procedure to count the records present in specific tables, here i will fetch the table name s from the text file.
Fo this i just write sample procedure to fetch th table names from text file and storing in tem table but while i am assigning and passing the table name i am unable to do it can any one suggest how to do it by simple example
You can build dynamic sql using table name parameter and execte it using EXEC as shown below.
Create procedure Proc1
#TableName varchar(MAX)
as
..
...
DECLARE #sqltxt varchar(1000)
select #sqltxt = 'select count(*) from ' + quotename(#TableName)
exec (#sqltxt)
...
...