Approach to import/export SQL Server stored procedures - c#

I know most people will give responses like, right click database name, select task, generate scripts, etc. Most people give detail response on the internet include stackoverflow
I already know that long time ago.
My question is any suggestions to write scripts to export/import stored procedures. If I only have to do 5 or 10, it is no big issue. I just have to pick right ones. If there are lots like 50 or more, it is pain and bound to be errors. Sometimes, to make life easier, we want to export/import with suffix such as *test.sql.
Any suggestion using SQL scripts, C# or dos commands to do so?
Thanks

Since you tagged also C#. With .NET you can do it using Microsoft.SqlServer.Smo. There is a StoredProcedure class. This approach gives you really lots of control over the task you want to achieve. Example usage you can find here, also using PowerShell.
Here's a snippet I've put together that might help you(uses Microsoft.SqlServer.Smo, Microsoft.SqlServer.ConnectionInfo, Microsoft.SqlServer.Management.Sdk.Sfc DLLs):
var serverConnection = new ServerConnection(new SqlConnection(ConnectionString));
var server = new Server(serverConnection);
foreach (StoredProcedure sp in server.Databases["YourDB"].StoredProcedures)
{
if (sp.Name.Contains("yourSubstring"))
{
System.IO.File.WriteAllText(sp.TextHeader + Environment.NewLine + sp.TextBody);
}
}

Why have you tagged this question with C# when you ask for answers in SQL or DOS script?
This CMD script drops all stored procedures whose names begin with "usp":
FOR /F "tokens=* USEBACKQ" %%F IN (`sqlcmd -S %SERVER% -d %DATABASE% -Q "select name from sys.objects where type = 'P'" `) DO (echo %%F | findstr "^usp" >nul 2>&1 && sqlcmd -S %SERVER% -d %DATABASE% -Q "drop procedure %%F" )
It's all on one line and %SERVER% and %DATABASE% identify the server and database.
This script loads all stored procedures from the current directory whose names match "usp*.sql". You will need the setting QUOTED_IDENTIFIER to be ON if you invoke XML data type methods.
for %%f in (usp*.sql) do sqlcmd -S %SERVER% -d %DATABASE% -I -i %%f

Related

Access a postgres table using the schema prefix in c#

I'm using postgres in a c# project and i'm trying to make some basic queries such as
qry.CommandText = "select * from LOGIN";
NpgsqlDataReader qryReader = qry.ExecuteReader();
But it says that the table LOGIN does not exist.
I already know that the following query works: qry.CommandText = "select * from \"myDataBase\".LOGIN"; but I would prefer not using it.
I also know from this thread that I can use SET search_path TO myschema,public; to access the table without prefix in psql command line, but I don't see how it would work for my c# projet.
Also, I have another project where I don't need the prefix but I don't know why it works for my other projet and not this one.
Any help would be very appreciated.
Thanks
While SET search_path TO myschema,public; sets the search_path for the current session only, you can make it permanent for the user running the command:
ALTER USER username SET search_path TO myschema, public;

Passing file name with spaces to SPOOL command using SQL Plus gives SP2-0768 Illegral SPOOL command error

My sql file contains
`SPOOL &1;
//sql code to execute
SPOOL OFF;`
The sql file is executed using SQL Plus and SQL Plus is being called from C# code using Process.Start... Code snipped
`var m_StartInfo = new ProcessStartInfo();
m_StartInfo.FileName = "SQLPLUS.EXE";
m_StartInfo.CreateNoWindow = true;
m_StartInfo.UseShellExecute = false;
m_StartInfo.Arguments = String.Format("{0}\"{1}\" \"{2}\"", connectionString, sqlfile, sqlLogFileName);
m_Process = Process.Start(m_StartInfo);
Other code.....`
It works fine and the sqlplus log is created fine at sqlLogFileName location. However if the sqlLogFileName has spaces in between (say like "C:\My Application\log.txt"), then the log file is not created, instead gives the error SP2-0768 Illegal SPOOL command on SQL Plus window
Any suggestion how to resolve this? I am using Oracle 11GR2
You just have to surround your file name with double quotes.
Something like:
spool "Test with spaces.txt"
Or in your case with a parameter:
SPOOL "&1"
I think is best not to use spaces though, as #tvCa explained.
Side Note:
What StarPilot is refering to, I believe, is about the redirect a command output to a file, and that is why it didn't work when you tried to use it.
For example in command prompt you would write:
dir > dir.txt
And that saves the output of dir inside dir.txt.
Oracle software is designed to be used with directories not having whitespaces (which is an accepted standard in Linux/Unix, even though technically you can do otherwise). On Windows, things are different, but the Oracle software has the same idea : it does not like whitespaces. So, the fix is clear : spool to a directory without whitespaces. This is advice, anybody is free to take or not.

Is there any way to update Sql Server Agent jobs schedule time by programmatically

presently we have many jobs are sheduling different time is there any way we can run all the jobs to schedule at same by programatically.
You can change the start time using T-SQL in the msdb..sysschedules table. Maybe you could create a stored procedure and call it from your C# code.
The snippet below will set all jobs to run at 6:30:45am.
-- times are stored as integers, eg. 63045 = 06:30:45
update s set
s.active_start_time = 63045
from msdb..sysjobs j
left join msdb..sysjobschedules js on j.job_id = js.job_id
left join msdb..sysschedules s on js.schedule_id = s.schedule_id
See dbo.sysschedules (Transact-SQL) for more info.
You should look into SMO. This class gives programmatic access to many of the things you can do in SQL Management Studio. Below is a link on how to start a job using SMO.
http://msdn.microsoft.com/en-us/library/ms202348.aspx
Full PowerShell script to change all jobs to run at 2:30am:
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo");
$svr = "."
$sql = new-object `
Microsoft.SqlServer.Management.Smo.Server `
$svr;;
$time = new-object -type "system.timespan" -argumentlist #(2,30,0)
$sql.jobserver.jobs | foreach-object {
$_.jobschedules[0].activestarttimeofday = $time
$_.jobschedules[0].alter()
}
$time can be tweaked as necessary, an IF statement can be added in the loop to only modify particular jobs, if the job has more than one schedule then it will need to be modified as the above only looks at the first schedule.

Problem with credential chaining sequence in Windows and .NET

Problem Summary
When run in batch mode, my user identity is lost but (like a really good cliffhanger) not until the very last moment.
Problem Details
I have a PowerShell script running on WinXpSp3 that runs a script block (via Invoke-Command) on a remote machine as a particular test user (via -Session parameter) in the background (via -AsJob parameter). The session is created with this:
New-PSSession -computername $myServer -credential $myCredential
The script block performs a number of actions, culminating in running the NUnit test framework. The C# code under test records the "myTestUser" username (via Environment.UserName) so the credentials provided by PowerShell are properly received that far. This is further confirmed by Process Explorer: examining properties of nunit-console running the batch tests shows it is owned by myTestUser.
The test includes accessing a Sql Server 2008 R2 database; the connection string is set via a new SqlConnection(connectionString) call. The connection string is set up for Windows Authentication and uses this form:
Data Source=<my_db_server_name>;Initial Catalog=<my_db_name>;Integrated Security=True;Persist Security Info=True
Even though I have conclusively pushed the myTestUser credentials all the way to the C# code under test, the DB access attempt is not seeing these credentials, resulting in this error: Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'
Some supplemental info:
I have confirmed that the test user (myTestUser) has DB permissions and the NUnit test is capable of accessing the DB: When I run the NUnit test manually (via NUnit GUI) logged in as myTestUser, the test works properly and SqlProfiler clearly shows this activity with myTestUser appearing in the NTUserName column.
The same error occurs if I run locally rather than on a remote machine.
The same error occurs if I run as myself on my local machine (i.e. omitting the -credential parameter).
Question
How can I rescue myTestUser from the brink of doom and get him DB access?
2011.05.16 Update
Here is a simplified example exhibiting the same problem.
First, my test program DBFetchVersion that prints the name of the current user and the results of a simple query:
class Program
{
const string connString = ...your connection string here... ;
const string query = "SELECT getdate() [Date], substring(##version,1,charindex('-',##version)-1) +convert(varchar(100),SERVERPROPERTY('edition'))+ ' ' +convert(varchar(100),SERVERPROPERTY('productlevel')) [SQL Server Version], ##servicename [Service Name], ##servername [Server Host], db_name() [Database], user_name() [User], host_name() [Client]";
static void Main(string[] args)
{
DataView dataView;
using (var connection = new SqlConnection(connString))
{
Console.WriteLine("user = " + Environment.UserName);
using (var dataAdapter = new SqlDataAdapter(query, connection))
{
var dataSet = new DataSet();
try
{
connection.Open();
dataAdapter.SelectCommand.CommandType = CommandType.Text;
dataAdapter.Fill(dataSet, query);
}
finally { if (connection.State == ConnectionState.Open) connection.Close(); }
dataView = dataSet.Tables[0].DefaultView;
}
foreach (var item in dataView.Table.Rows[0].ItemArray)
Console.WriteLine(item);
}
}
}
And here is the Powershell script that calls the above program.
$scriptBlock = {
& "...path to my executable...\DBFetchVersion\bin\Debug\DBFetchVersion.exe"
}
$serverName = ... my server name ...
$username = "testuser"
$password = ... my user password ...
$adjPwd = $password | ConvertTo-SecureString -asPlainText -Force
$testCred = (New-Object System.Management.Automation.PSCredential($username,$adjPwd))
$mySession = New-PSSession -computername $serverName -credential $testCred
# Test Scenarios:
Invoke-Command $scriptBlock
#Invoke-Command $scriptBlock -computername $serverName
#Invoke-Command $scriptBlock -computername $serverName -credential $testCred
#Invoke-Command $scriptBlock -Session $mySession
In the list of four test scenarios at the end, the uncommented one works, printing my user name and the results of the query.
DBFetchVersion still reports I am the user with the second line, but the DB connection fails with the " Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON' " error.
The remaining two lines report the "testuser" user name, but both report the same login failure for the DB connection.
What this isolated example tells me is not that I think there is anything buggy about Powershell, .NET, or my code, but there is something with the authentication mechanism that I do not yet understand, since specifying another computer or a session both involve a path that should, in some sense, have stronger protection.
2011.08.03 Update - Eureka!
Well, Matt was correct in identifying the double-hop issue as the culprit and CredSSP authentication as the solution. Unfortunately, as I quickly found out, CredSSP requires Windows 7, so I went about setting up a couple VMs as a sandbox. CredSSP, however, was not one to easily relinquish its secrets (at least to me) as I detailed in this post on ServerFault: Cannot get CredSSP authentication to work in PowerShell
I finally was able to get CredSSP authentication to work so I could then come back to the problem I posed here in this thread. As a test, I used these 3 script blocks plugged into the PowerShell script I provided above:
$scriptBlockA = {
Write-Host ("hello, world: {0}, {1}" -f $env:USERNAME, (hostname))
}
# Simple DB test, but requires SqlServer installed!
$scriptBlockB = {
if (! (Get-PSSnapin | ? { $_.name -eq "SqlServerCmdletSnapin100" } ) )
{ Add-PSSnapin SqlServerCmdletSnapin100; }
Invoke-Sqlcmd -Query "SELECT getdate() as [Now]" -ServerInstance CinDevDB5
}
# Indirect DB test; requires .NET but not SqlServer,
# plus DBFetchVersion in home dir for targeted user.
$scriptBlockC = {
& ".\DBFetchVersion.exe"
}
Block A worked with or without CredSSP, since there is no double-hop. Blocks B and C would only work with CredSSP because they both attempt to access a remote database. QED.
Initially i read this and thought of the "double hop" issue, but the supplemental info maybe me question that though.
When you run it locally (as yourself or the testuser) what commands do you use? this:
& "...path to my executable...\DBFetchVersion\bin\Debug\DBFetchVersion.exe"
also does this work from your local machine (as either yourself or the user):
Add-PSSnapin SqlServerCmdletSnapin100;
Invoke-Sqlcmd -Query "SELECT getdate()" -ServerInstance Server
Also what OS are you using? If it is Windows 2008 and the issue is double hop you may be able to us CredSSP to avoid it.

How can I execute a .sql from C#?

For some integration tests I want to connect to the database and run a .sql file that has the schema needed for the tests to actually run, including GO statements. How can I execute the .sql file? (or is this totally the wrong way to go?)
I've found a post in the MSDN forum showing this code:
using System.Data.SqlClient;
using System.IO;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string sqlConnectionString = "Data Source=(local);Initial Catalog=AdventureWorks;Integrated Security=True";
FileInfo file = new FileInfo("C:\\myscript.sql");
string script = file.OpenText().ReadToEnd();
SqlConnection conn = new SqlConnection(sqlConnectionString);
Server server = new Server(new ServerConnection(conn));
server.ConnectionContext.ExecuteNonQuery(script);
}
}
}
but on the last line I'm getting this error:
System.Reflection.TargetInvocationException:
Exception has been thrown by the
target of an invocation. --->
System.TypeInitializationException:
The type initializer for ''
threw an exception. --->
.ModuleLoadException:
The C++ module failed to load during
appdomain initialization. --->
System.DllNotFoundException: Unable to
load DLL 'MSVCR80.dll': The specified
module could not be found. (Exception
from HRESULT: 0x8007007E).
I was told to go and download that DLL from somewhere, but that sounds very hacky. Is there a cleaner way to? Is there another way to do it? What am I doing wrong?
I'm doing this with Visual Studio 2008, SQL Server 2008, .Net 3.5SP1 and C# 3.0.
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
You shouldn't need SMO to execute queries. Try using the SqlCommand object instead. Remove these using statements. Use this code to execute the query:
SqlConnection conn = new SqlConnection(sqlConnectionString);
SqlCommand cmd = new SqlCommand(script, conn);
cmd.ExecuteNonQuery();
Also, remove the project reference to SMO. Note: you will want to clean up resources properly.
Update:
The ADO.NET libraries do not support the 'GO' keyword. It looks like your options are:
Parse the script. Remove the 'GO' keywords and split the script into separate batches. Execute each batch as its own SqlCommand.
Send the script to SQLCMD in the shell (David Andres's answer).
Use SMO like the code from the blog post.
Actually, in this case, I think that SMO may be the best option, but you will need to track down why the dll wasn't found.
MSVCR80 is the Visual C++ 2005 runtime. You may need to install the runtime package. See http://www.microsoft.com/downloads/details.aspx?FamilyID=200b2fd9-ae1a-4a14-984d-389c36f85647&displaylang=en for more details.
In addition to resolving the DLL issue and Matt Brunell's answer (which I feel is more appropriate for what you're trying to do), you can use the SQLCMD command line tool (from the SQL Client tools installation) to execute these SQL scripts. Just be sure it's on your path so you don't struggle with path locations.
This would play out like so:
Actual command:
SQLCMD -S myServer -D myDatabase -U myUser -P myPassword -i myfile.sql
Parameters (case matters):
S: server
d: database
U: User name, only necessary if you don't want to use Windows authentication
P: Password, only necessary if you don't want to use Windows authentication
i: File to run
Code to execute SQL files:
var startInfo = new ProcessStartInfo();
startInfo.FileName = "SQLCMD.EXE";
startInfo.Arguments = String.Format("-S {0} -d {1}, -U {2} -P {3} -i {4}",
server,
database,
user,
password,
file);
Process.Start(startInfo);
See http://msdn.microsoft.com/en-us/library/ms162773.aspx for more information on the SQLCMD tool.
Having the same need to automatically run a generated database script from code, I set out to parse the SQL script to remove GO statements and split the script into separate commands (as suggested by #MattBrunell). Removing the GO statements was easy, but splitting the statements on "\r\n" did not work since that screwed up the multiline-statements. Testing a few different approaches, I was quite surprised to find out that the script doesn't have to be split into separate commands at all. I just removed all "GO" statements and sent the whole script (including comments) into SqlCommand:
using System.Data.SqlClient;
using(SqlConnection connection = new SqlConnection(connectionString))
using(SqlCommand command = connection.CreateCommand())
{
string script = File.ReadAllText("script.sql");
command.CommandText = script.Replace("GO", "");
connection.Open();
int affectedRows = command.ExecuteNonQuery();
}
This code has been tested with SQL Server 2008 R2 and the script generated by "Database -> Tasks -> Generate Scripts...". Below are some examples of the commands in the script:
USE [MyDatabase]
ALTER TABLE [MySchema].[MyTable] DROP CONSTRAINT [FK_MyTable_OtherTable]
DROP TABLE [MySchema].[MyTable]
SET ANSI_NULLS ON
SET QUOTED_IDENTIFIER ON
/****** Object: Table [MySchema].[MyTable] Script Date: 01/23/2013 13:39:29 ******/
CREATE TABLE [MySchema].[MyTable](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Subject] [nvarchar](50) NOT NULL,
[Body] [nvarchar](max) NOT NULL,
CONSTRAINT [PK_MyTable] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
SET IDENTITY_INSERT [MySchema].[MyTable] ON
INSERT [MySchema].[MyTable] ([Id], [Subject], [Body]) VALUES (1, N'MySubject', N'Line 1
Line 2
Line 3
Multi-line strings are also OK.
')
SET IDENTITY_INSERT [MySchema].[MyTable] OFF
I guess there might be some maximum length for a single SqlCommand, above which the script have to be split. My script, which execute without problems, contains around 1800 statements and is 520 kB.
Have you tried running this with a very, very basic script in the .sql file? Maybe something that just inserts one row or creates an arbitrary table? Something that is very easy to verify? Essentially, this code is like hard coding the sql, except you're reading it from a file. If you can get it to work with a very simple file, then I would say that there is likely something wrong with the file structure itself. The post alluded to the fact that there are some stipulations regarding what can and cannot be in the file. If nothing else, it's a good place to start troubleshooting.
If you add following references in your project, then original code will work fine.
I use SQL 2008 Express.
Path:
C:\Program Files (x86)\Microsoft SQL Server\100\SDK\Assemblies\
Files:
microsoft.sqlserver.smo.dll, microsoft.sqlserver.connectioninfo.dll and Microsoft.SqlServer.Management.Sdk.Sfc.dll
You may be interested in this:
Link
It presents a general-purpose 'test fixture' to automatically execute sql-scripts. There is also sample code available, and there are no dependencies to any unusual assemblies whatsoever...

Categories