SQL Server Output multiple CSV files from one query - sql-server

I am trying to get SQL server to create multiple CSV files from one query. At this time we have 7 separate long running (2+ hours each) queries that need to be output to separate CSV files. I can create one query that calls all of them but that generates one giant CSV. Is there a way to tell SQL Server to create a separate CSV after each internal query has completed?
This question becomes more important as our next production run will have 52 of those long running queries and my boss does not want to have to run each of them separately.
EXEC dbo.Get_Result_Set1;
EXEC dbo.Get_Result_Set2;
EXEC dbo.Get_Result_Set3;
EXEC dbo.Get_Result_Set4;
EXEC dbo.Get_Result_Set5;
EXEC dbo.Get_Result_Set6;
EXEC dbo.Get_Result_Set7;

You may want to create an SSIS package as the wrapper around executing these stored procedures, rather than your current query.
Each stored procedure can then be linked to a flat-file output.
This has the advantage that you should be able to express any required dependencies between the different invocations and (if possible) exploit some parallelism (if there are no dependencies between some/all of the invocations).

Could you create an Agent Job to do it? You could do a separate job step for each one of the queries. Under the advanced tab in the step section, there is an output option.

Not the answer I was looking for but I wound up having someone help me write a C# procedure in Visual Studio that calls each of my SQL procedures and outputs each to an Excel file. It works and I can reuse it in the future.
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
namespace StoredProcedureRunner
{
class Program
{
public static void Main(string[] args)
{
var storedProcs = new List<string>();
storedProcs.Add "dbo.Get_Result_Set1");
storedProcs.Add "dbo.Get_Result_Set2");
storedProcs.Add "dbo.Get_Result_Set3");
storedProcs.Add "dbo.Get_Result_Set4");
storedProcs.Add "dbo.Get_Result_Set5");
storedProcs.Add "dbo.Get_Result_Set6");
storedProcs.Add "dbo.Get_Result_Set7");
foreach (var storedProc in storedProcs)
{
var table = GetDataTable(storedProc);
WriteDataTableToCSV(storedProc + ".csv", table);
}
}
public static DataTable GetDataTable(string storedProc)
{
DataTable table = new DataTable();
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ConStrg"].ConnectionString))
{
using (var command = new SqlCommand(storedProc, connection))
{
using (var adapter = new SqlDataAdapter(command))
{
command.CommandType = CommandType.StoredProcedure;
command.CommandTimeout = 0
adapter.Fill(table);
}
}
}
return table;
}
public static void WriteDataTableToCSV(string filename, DataTable table)
{
StringBuilder sb = new StringBuilder();
var columnNames = table.Columns.Cast<DataColumn>().Select(col => col.ColumnName);
sb.AppendLine(string.Join(",", columnNames));
foreach(DataRow row in table.Rows)
{
var fields = row.ItemArray.Select(field => field.ToString());
sb.AppendLine(string.Join(",", fields));
}
File.WriteAllText(filename, sb.ToString());
}
}
}

Related

C# Snowflake command

What is the syntax for these two C# Snowflake commands.
Or where can i find it
using (var sfConn = new SnowflakeDbConnection())
{
sfConn.ConnectionString = sConnectionString;
sfConn.Open();
var cmd = sfConn.CreateCommand();
cmd.CommandText = "select top 100 * from RDW_SF.DM_LOAN.DIM_STD_MTHLY_DTM ;";
SnowflakeDbCommand sfcmd = new SnowflakeDbCommand(sfConn.ConnectionString);
SnowflakeDbCommandBuilder sf = new SnowflakeDbCommandBuilder(,)
// cmd.CommandText = "select top 100 * from RDW_SF.DM_LOAN.DIM_ACCT_CNTC;";
// DWLoad_Load_DM_LOAN_DIM_STD_MTHLY_DTM)
var reader = cmd.ExecuteReader();
Console.WriteLine("Retrieving rows from the Query");
SnowflakeDbDataAdapter mySnowflakeDbDataAdapter = new SnowflakeDbDataAdapter(csfcmd);
DataSet myDataSet = new DataSet();
mySnowflakeDbDataAdapter.Fill(myDataSet, "RECS");
sfConn.Close();
DataTable myDataTable = myDataSet.Tables["RECS"];
nCols = myDataTable.Columns.Count;
}
If you're looking for code examples, the snowflake-connector-net repository carries test-cases that exercise its features.
Example of a simple SnowflakeDbCommand
Similar example, with a DataReader
A DataSet and DbDataAdapter example
The Snowflake connector for .NET implements the standard and familiar .NET System.Data interfaces so any example online that uses them (across database types) can be adapted for use with Snowflake with simple SQL-syntax changes (to conform to Snowflake's SQL).
If you're looking for examples on how to perform a connection, you can find an example connection string here.

Foreach Loop container in SSIS suggestion

I have for-each loop container in my SSIS master package
There is 'Execute Package Task'
under this 'Execute SQL Server Agent Job Task' is there.(It has 35 steps)
Problem:
SSIS master package is starting next iteration without completion of all the steps in 'Execute SQL Server Agent Job Task'.
Please suggest an approach to start the next iteration after completion of all the steps only.
Thanks in advance :)
I know I keep throwing C# solutions at SSIS problems but this is exactly what I do to prevent a job/process to run while a job is running.
I have a function that does this check and returns a boolean true/false. Use that result to determine whether to start the job or not.
public static bool isDatabaseUpdating()
{
List<string> jobs = new List<string>();
jobs.Add("[Insert name of Job here]");
jobs.Add("[Insert another job and so on here]");
string sql = "exec msdb.dbo.sp_help_job #execution_status = 1";
using (OleDbConnection conn = new OleDbConnection(cstr))
{
using (OleDbCommand cmd = new OleDbCommand(sql, conn))
{
cmd.CommandType = CommandType.Text;
cmd.Connection.Open();
OleDbDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
if (jobs.Contains(rdr["name"].ToString())) return true;
}
}
}
return false;
}
To use it set a SSIS Variable like this:
Dts.Variables["#variableName"].Value = isDatabaseUpdating();
And then in control flow set expression on path appropriately.
The real key to understanding this function is the SQL.
exec msdb.dbo.sp_help_job #execution_status = 1
That returns a dataset of jobs that are currently running.
Enhancement to your application
This is what your control flow will look like:

Require identify report using number of columns in the excel report

I have two excel files and I want to import these files to SQL temporary table.
First excel file:
T1 T2 T3 T4 Total
1,472 1,364 1,422 – 4,258
-152.6 -152.6 -152.6 –
1,958 1,939 1,942 –
-122.6 -123.7 -122.2 –
Second excel file:
T1 T2 T3 T4 T5 Total
1,472 1,364 1,422 – 12.2 4,258
-152.6 -152.6 -152.6 – 1000.12
1,958 1,939 1,942 – 50.23
-122.6 -123.7 -122.2 – 185.25
Is there any way in SSIS to identify the files on the basis of number of columns? I need to identify report on the basis of the column number.
Objects from the Microsoft.Office.Interop.Excel namespace can be used in a C# Script Task to do this as follows. This example outputs the file name and number of columns into an SSIS object variable (User::SSISObjectVariable") that can be used to apply further logic and processing in the package, such as storing in a database table or otherwise. The full file path is the first column in the object variable and the count of columns is the second. Also be sure to add a reference to the Microsoft.CSharp namespace in the script. The object variable will need to be included in the ReadWriteVariables field of the Script Task and if the source folder is stored in a variable (as done below) then add this variable in the ReadOnlyVariables field.
using Microsoft.Office.Interop.Excel;
using System.Data;
using System.IO;
using System.Collections.Generic;
List<string> excelFileList = new List<string>();
//get source directory
string filePath = Dts.Variables["User::FilePathVariable"].Value.ToString();
DirectoryInfo di = new DirectoryInfo(filePath);
System.Data.DataTable dt = new System.Data.DataTable();
dt.Columns.Add("FilePath", typeof(System.String));
dt.Columns.Add("ColumnCount", typeof(System.Int32));
foreach (FileInfo fi in di.EnumerateFiles())
{
//optional- check file extension and prefix
if (fi.Extension == ".xls" && fi.Name.StartsWith("Prefix"))
{
//get full file path
excelFileList.Add(fi.FullName);
}
}
foreach (string excelFile in excelFileList)
{
Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application(); ;
Microsoft.Office.Interop.Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(excelFile);
Microsoft.Office.Interop.Excel.Worksheet xlWorksheet = xlWorkbook.Sheets[1];
int columnCount;
//get number of columns
columnCount = xlWorksheet.Cells.Find("*", System.Reflection.Missing.Value,
System.Reflection.Missing.Value, System.Reflection.Missing.Value,
Microsoft.Office.Interop.Excel.XlSearchOrder.xlByColumns, Microsoft.Office.Interop.Excel.XlSearchDirection.xlPrevious,
false, System.Reflection.Missing.Value, System.Reflection.Missing.Value).Column;
//build data row to hold file path and column count
DataRow dr = dt.NewRow();
dr["FilePath"] = excelFile;
dr["ColumnCount"] = columnCount;
dt.Rows.Add(dr);
xlApp.Workbooks.Close();
xlApp.Quit();
xlWorkbook = null;
xlApp = null;
}
GC.Collect();
GC.WaitForPendingFinalizers();
//populate object variable
Dts.Variables["User::SSISObjectVariable"].Value = dt;
If you need to import excels with different schemas, you have two approaches:
(1) SSIS approach: Script Task + 2 Data flow tasks
In case that you only have two structures, then you can follow these steps:
Add a variable of type System.Int32 example: #[User::ColumnsCount]
Add a variable of type System.String to store the file path example: #[User::FilePath]
Add a script Task with and Select #[User::FilePath] as ReadOnly variable and #[User::ColumnsCount] as ReadWrite Variable
Inside the script Task write a similar script:
string FilePath = Dts.Variables["User::FilePath"].Value.toString();
string ExcelConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;
"Data Source='" + FilePath +
"';Extended Properties=\"Excel 12.0;HDR=YES;\"";
using (OleDbConnection OleDBCon = new OleDbConnection(ExcelConnectionString))
{
if (OleDBCon.State != ConnectionState.Open)
OleDBCon.Open();
using (OleDbCommand cmd = new OleDbCommand(strcommand, OleDBCon))
{
DataTable dtTable = new DataTable("Table1");
cmd.CommandType = CommandType.Text;
//replace Sheet1$ with the sheet name if it is different
cmd.CommandText = "SELECT * FROM Sheet1$"
using (OleDbDataAdapter daGetDataFromSheet = new OleDbDataAdapter(cmd))
{
daGetDataFromSheet.FillSchema(dtTable, SchemaType.Source);
Dts.Variables["User::ColumnsCount"].Value = dt.Columns.Count;
}
}
}
Add two Data Flow task, on for each Excel structure
Link the Script Task to each one of these Data Flow Task
Click on each Precedence constraint (link between tasks) and change the precendence type to Expression and Constraint and add the appropriate expression for each case:
5 columns:
#[User::ColumnsCount] == 5
6 columns:
#[User::ColumnsCount] == 6
Set the Delay Validation property to True for both Data Flow Tasks
TL DR: In case that you only have two structures, you can add two Data Flow Tasks (one for each structure), then you can use a Script Task to identify the columns count and execute the appropriate Data Flow Task based on the Columns Count (using precedence constraints expressions).
(2) C# approach: SchemaMapper class library
Recently i started a new project on Github, which is a class library developed using C#. You can use it to import tabular data from excel, word , powerpoint, text, csv, html, json and xml into SQL server table with a different schema definition using schema mapping approach. check it out at:
SchemaMapper: C# Schema mapping class library
You can follow this Wiki page for a step-by-step guide:
Import data from multiple files into one SQL table step by step guide

Viewing a file stored in Varbinary(MAX) in SQL Server

Let's say I inserted a file into a varbinary(max) field like so:
CREATE TABLE myTable
(
FileName nvarchar(60),
FileType nvarchar(60),
Document varbinary(max)
)
GO
INSERT INTO myTable(FileName, FileType, field_varbinary)
SELECT
'Text1.txt' AS FileName,
'.txt' AS FileType,
*
FROM
OPENROWSET(BULK N'C:\Text1.txt', SINGLE_BLOB) AS Document
GO
Of course my file now looks like this:
0xFFD8FFE000104A46494600010101004800....
Is there a simple and elegant way to retrieve this file?
My preference is to open it right away in a temp folder rather than saving and then viewing and deleting. In MS Access, this is as simple as using an Attachment field and double clicking to upload/download.
Since there is no built-in functionality in SSMS for this task I usually write a simple LINQPad script that extracts my varbinary column and writes it to the file system.
Something like this:
var results = from p in myTable
where p.ID == ... //your condition here
select p;
foreach (var item in results)
{
File.WriteAllBytes("C:\\" + item.FileName + item.FileType , item.Document.ToArray());
}
I am working with C# and ASP.NET, and I ended up doing this using a Generic Handler, later linking to it elsewhere in my website project:
public class ImageProvider : IHttpHandler {
public string connString = "...";
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "image/jpeg";
string sqlSelectQuery = "select img from table"
SqlConnection conn = new SqlConnection(connString);
conn.Open();
SqlCommand cmd = new SqlCommand(sqlSelectQuery, conn);
byte[] img = (byte[])cmd.ExecuteScalar();
context.Response.BinaryWrite(img);
}

ServiceStack Ormlite transactions broken?

I am using ServiceStack.Ormlite for SQL Server and just updated from 3.9.71 to 4.0.33.0 and now transactions for direct commands are failing. I can get ORMlite transactions working or direct commands, but not both.
The complication is I am doing some very complicated DB commands and since Sql.In() for a large list of GUIDs is massively slow I have a workaround which uses db.CreateCommand() and then passes the GUID list in as a custom table type.
Thus I need a single transaction to span across ORMLite commands and direct db commands. The following code used to work and now it fails.
For instance the following code used to work. I now get errors saying that the CreateCommand() should use the transaction. When I directly try then I get the indicated cast exception:
using (var db = DB.Connection.OpenDbConnection())
{
using (var transaction = db.OpenTransaction())
{
// Some ORMLite code
db.Delete<SomeType>();
using (var command = db.CreateCommand())
{
// Direct DB command
command.CommandText = "Delete from SomeTable where ...";
command.Parameters.Add(GUIDList)
command.ExecuteNonQuery();
}
}
}
Clarification:
In the code OpenTransaction() will work for the OrmLite code, but fail on the CreateCommand code. BeginTransaction() will fail for the OrmLite code.
The actual error is at command.ExecuteNonQuery(): ExecuteNonQuery requires the command to have a transaction when the connection assigned to the command is in a pending local transaction. The Transaction property of the command has not been initialized.
To use Transactions in OrmLite you should use the OpenTransaction() API, e.g:
using (var trans = db.OpenTransaction())
{
//...
}
I've added a couple new API's to be able to use an OrmLite transaction with a raw ADO.NET IDbCommand in this commit.
Use a managed OrmLite DB Command
Use a managed OrmLite command with OpenCommand() which will automatically prepopulate the DB command with the current transaction, e.g:
using (var trans = db.OpenTransaction())
using (var command = db.OpenCommand())
{
command.CommandText = "Delete from SomeTable where ...";
}
Manually assign underlying DB Transaction
When using the underlying ADO.NET IDbCommand you will need to also manually assign the Transaction to the command yourself, i.e:
using (var trans = db.OpenTransaction())
using (var command = db.CreateCommand())
{
command.Transaction = trans.ToDbTransaction();
command.CommandText = "Delete from SomeTable where ...";
}
The ToDbTransaction() extension method lets you access the underlying ADO.NET IDbTransaction which is required when using the underlying ADO.NET IDbCommand.
Both of these new API's are available from v4.0.34+ that's now available on MyGet.
Here is my suggestion that works.It is based on previous answers
IDbConnection conn = DB.Connection;
IDbCommand cmd = conn.CreateCommand();
using (IDbTransaction transaction = conn.OpenTransaction())
{
//ADO.NET code
cmd.Transaction = transaction.ToDbTransaction();
cmd.CommandText = "...Some sql text";
cmd.executeNonQuery();
// Some ORMLite code
conn.Delete<SomeType>();
}

Resources