sql server - setting variables at runtime - sql-server

In Oracle you can use &&VAR_NAME in a script and then the script will ask you for that value when you run it.
In SQLSERVER you can use $(VAR_NAME) and reference a property file using:
:r c:/TEMP/sqlserver.properties
And in the property file you have something like:
:setvar VAR_NAME_some_value
Can you do the equivalent of &&VAR_NAME so the script asks you for the value when you run it instead of having the value predefined in a script.

If I've understood correctly, you're talking about variable substitution with the SQLCMD utility.
I don't see that SQLCMD supports the behaviour you describe.
An alternative would be to exploit the fact that SQLCMD will substitute the values of system or user environment variables (see the link above), and create a wrapper CMD script which prompts the user for the variable value(s) using SET with the /P flag.

There is nothing in sql server like this, you should predefine all parameters values before using them, like this:
DECLARE #i SMALLINT
SET #i = 1

The problem with having a form pop up and ask you for the parameter is that you normally want rather more control over the nature of the form, even for an admin script. I'd use the variable substitution in SQLCMD, from within a Powershell or Python script so you can provide the guy running the script a better and more helpful form. That would make a very powerful combination.
You can do quite a lot with template variable substitution in SSMS, but that would only go so far as formulating the correct SQL to execute. you'd then have to bang the button. It would be a bit clunky outside the development environment!

Related

How to import file contents into table column as data

A project I'm working on at work involves modifying one of the subsystems to store/pull data that is currently stored in files into the database. Each of the files is a single, sometimes-large, chunk of custom (xml-based) script generated by another custom tool.
Conceptually, I'm looking for an easy way to do something like:
For Each file in folder_and_subfolders
INSERT INTO table
(script_name, version_num, script )
VALUES
({file_name}, 1, {file_contents})
;
Next
Preferably on an entire directory tree at once.
If there's no easy way to do this via T-SQL, I can write a utility to do the job, but I'd prefer something that didn't require having to write another custom tool that will only be used once.
So, I don't have SQL Server installed and therefore can't test this, but if you are looking for a simple batch file that could do what you're after, I'd suggest something like the following might well help;
#echo off
SET xmldir=./myxmlfiles/live/here/
echo --- Processing files
for %%f in ("%xmldir%*.xml") do (echo Running %%f.... && #sqlcmd -I -U %1 -P %2 -S %3 -d %4 -v filename="%xmldir%%%f" -i ProcessFile.sql)
I'm not sure how much you know about sqlcmd, but it is a command line tool that is generally provided by SQL Server. It will allow you to run SQL commands, or in the case above, run a script which is indicated by the -i parameter. I am assuming that you'd place your SQL statement in there to perform your additions to the table.
The other parameters to sqlcmd are described below;
-I sets QUOTED_IDENTIFIER on (you may or may not need this. I did for an earlier issue I faced with sqlcmd and QUOTED_IDENTIFIER)
-U sets the database username
-P sets the database password
-S sets the database server
-d sets the database to connect to
-v is the interesting one here as it lets you pass parameters to your script. Note that on the MSDN page describing this, it states that if your path or filename contains spaces, then you'll need to enclose it in quotes, so check that out. Basically though, you'd be able to refer to the parameter inside your sql script (ProcessFile.sql) like INSERT INTO mytable (file_name) VALUES ('$(filename)')
You'd have to use the logic described in the answer from my previous comment to ensure

SQLCMD - Capture output from Operating System Command as variable

I would like to capture the output of an sqlcmd !! (operating system command) call into a variable that I can use in an insert statement. Or, just read the contents of a file into a variable.
The general idea is something like this:
:SETVAR version !! "type version.txt"
insert into dbo.DeployVersion ([Version],[Date],[User]) values ('$(version)',getdate(),'$(SQLCMDUSER)')
But it doesn't seem that I can chain SQLCMD calls in that way. Any ideas?
type version.txt just prints the content of version.txt file to the console, like cat in linux.
The best I could find was this approach, which seemed really silly.
:setvar quot "'"
declare #versionString nvarchar(300)
set #versionString =
$(quot)
:r .\version.txt
$(quot)
insert into dbo.DeployVersion ([Version],[Date],[User]) values ('$(version)',getdate(),'xyz')
This works in purely SQLCMD and is an answer to the original question.
However, this is being used in an sqlpackage deploy and I don't see a way to externally reference a version.txt file from outside of the dacpac. So instead I added a variable to the dacpac project and I specify that value on the commandline using sqlpackage ... /Variables:Version=%someValueReadFromBatFile%

Check if a file exists in UNIX from SQLplus without UTL_FILE

My current scenario is like this:
I need to login to sqlplus from a shell script to call a stored procedure.
After that I need to create a CSV file by SPOOLING data from a table.
Then I need to check whether the CSV file has been created in a particular directory and depending on the result an update query needs to be run.
I know that this can be checked within sqlplus with the help of UTL_FILE package but unfortunately due to Client policies,the access of this package is restricted in the current system.
Another way is to exit from sqlplus and perform the file check in UNIX and then again log in to sqlplus to perform the rest actions. But this I believe would result in slower execution time and performance is an important factor in this implementation as the tables contain huge volumes of data(in millions).
So is there any other way to check this from sqlplus without exiting from the current session?
System Info:
OS - Red Hat Enterprise Linux
Database - Oracle 11g
If the file is on the same machine that you're running SQL*Plus on, you could potentially use the host command.
If the file you're checking is the same one you're spooling to, it must exist anyway, or you would have got an SP error of some kind; but if you do want to check the same file for some reason, and assuming you have a substitution variable with the file name:
define csv_file=/path/to/spool.csv
-- call procedure
spool &csv_file
-- do query
spool off
host ls &csv_file
update your_table
set foo=bar
where &_rc = 0;
If the file exists when the host command is run, the _rc substitution variable will be set to zero. If the file doesn't exist or isn't readable for any reason it will be something else - e.g. 2 if the file just doesn't exist. Adding the check &_rc = 0 to your update will mean no rows are updated if there was an error. (You can of course still have whatever other conditions you need for the update).
You could suppress the display of the file name by adding 1>/dev/null to the host command string; and could also suppress any error messages by also adding 2>/dev/null, though you might want to see those.
The documentation warns against using &_rc as it isn't portable; but it works on RHEL so as long as you don't need your script to be portable to other operating systems this may be good enough for you. What you can't do, though, is do anything with the contents of the file, or interpret anything about it. All you have available is the return code from the command you run. If you need anything more sophisticated you could call a script that generates specific return codes, but that's getting a bit messy.

Setting sqlcmd variable using tsql variable

It seems like this approach for setting sqlcmd variable does not work so I am wondering if there is another way?
DECLARE #d varchar(max);
SET #d = 'foobar';
:setvar database #d
USE $(database)
Note: I realized that using :setvar in this manner works but I want to set it using a variable in the T-SQL script instead of explicitly setting it.
:setvar database "foobar"
This makes no logical sense, from the perspective of SQLCMD. :setvar is a command that lives outside the T-SQL script; it can't "see" anything the T-SQL script does at runtime. You can set the variable through any number of means, including environment variables, options and :setvar itself, but you can't mix in runtime T-SQL execution.
Rethink your script so the SQLCMD script lives "outside" the T-SQL script, and the T-SQL script does the necessary runtime actions. Since you can execute dynamic statements in T-SQL, there shouldn't be any need for setting an SQLCMD variable through T-SQL rather than the other way around.

Tracing Batch Variable

I have this batch which executes on the server computer. There is a scheduled job which runs the batch. The Batch detects a particular file and then it executes an sqlcmd like below:
if not exist %TRIG_FILE% goto No_Triggers
sqlcmd
-S %WSL_SERVER%
-d %WSL_DATABASE%
-E
-Q "DECLARE #RES integer;DECLARE #RET varchar(1);DECLARE #MSG varchar(65);EXEC Ws_Job_Release 1,'Release Job Unlock Batch','All',0,0,'Unlock_Batch',#RET OUTPUT,#MSG OUTPUT,#RES OUTPUT"
My question is - how did the batch know what the %WSL_SERVER% variable is, because when I look at the script, there is nowhere in there which sets the %WSL_SERVER% variable.
This is the first time I'm reading a .bat script, I know a fair bit of programming, but I can't see how that variable was passed into this script so that it knows which server. There's no other batch calling this, it's from the batch run by the scheduler.
thanks
gemmo
Most likely the WSL_SERVER and WSL_DATABASE are global environment variables initialised every time with your Windows session. That means they exist (are defined) in every CMD session and thus in every batch script. You can open a new Command Prompt window and issue this command
SET WSL
which will (try to) display all environment variables, whether global or local, whose names start with WSL. My guess is the output will show you at least the two WSL variables used in your script.
There is a number of global variables pre-defined and maintained by the OS. Yours, however, are probably user-defined (just my guess based on the fact that my system does not have them). User-defined variables can be created by third-party software or your own (maybe someone else's) batch scripts, as well as with a standalone invocation of the SETX command:
SETX VarName "Value"
You can use that command to change the value of any of your variables globally. Note that you can also change that value temporarily only, for the duration of the script, using the SET command as usual, if global change is undesirable:
SET "VarName=Value"

Resources