Parametrize query in t-sql - sql

SELECT TOP #columnCount #columnName
FROM #tableName
I get the following error
Incorrect syntax near '#columnCount'.
What could be wrong?
If I change to
SELECT TOP (#columnCount) #columnName
FROM #tableName
I get the following error
Must declare the table variable "#tableName".
I run it from C#

A safe and secure way would be
DECLARE #columnCount INT = 100
DECLARE #columnName NVARCHAR(128) = 'YourColumnName'
DECLARE #tableName NVARCHAR(128) = 'YourTableName'
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N'SELECT TOP (#columnCount) ' + QUOTENAME(#columnName) + N'
FROM ' + QUOTENAME(#tableName)
EXECUTE sp_executesql #Sql
,N'#columnCount INT'
,#columnCount

You need dynamic SQL to accomplish what you're trying to do.
DECLARE #sql VARCHAR(max);
SET #sql = 'SELECT TOP ' + #columnCount + ' ' + #columnName + ' FROM ' + #tableName;
EXEC(#sql);
The variables used need to be converted appropriately.
Read more in the documentation

Column lists and Table names cannot be parameters. However, since you are running this from C# you are technically already using Dynamic SQL (unless you are calling a stored procedure with those params but there is no mention here of stored procedures being used so for now I will assume not). When building the SQL in C#, you need to concatenate the Column List and Table Name into the query but you can still use a parameter for the value used by the TOP() operator:
SqlConnection _Connection = new SqlConnection("connection string");
SqlCommand _Command = new SqlCommand();
SqlDataReader _Reader = null;
string _Query;
string _TableName = "dbo.MyTable";
string _ColumnList = "Field1, Field2 AS [AliasedName], Field3";
int _NumberOfRows = 12;
_Query = String.Concat("SELECT TOP (#NumberOfRows) ",
_ColumnList, " FROM ", _TableName);
SqlParameter _NumRows = new SqlParameter("#NumberOfRows", SqlDbType.Int);
_NumRows.Value = _NumberOfRows;
try
{
_Connection.Open();
_Reader = _Command.ExecuteReader();
// do stuff
}
finally
{
_Reader.Close();
_Connection.Close();
}
Of course, you could also just concatenate the #NumberOfRows value directly into the query as well, but keeping it as a parameter will allow for Query Plan re-use if running this query multiple times with the same values for ColumnList and TableName but changing the #NumberOfRows value.

Related

SQL smart search possible?

I am trying to pull a list from a large repository. And if there are spaces in this search, I separate the spaces and search separately. So far everything is working fine. And I am sending a list to the JS side to highlight the searched words. The word in the example search is 'paston'. I need to search for it as 'piston' or 'peston', is it possible? The only way I could think of was to change each letter of the words coming from the search in SQL to _ respectively and add them to the end with or, like '_iston' or 'p_ston' or 'pi_ton'. but I'm not sure if this works slow. Does anyone have any suggestions or know of a ready-made structure in SQL?
My SQL code is like this:
ALTER PROCEDURE [dbo].[GetStoklar]
#PageIndex nvarchar(15)
,#PageSize nvarchar(15)
,#Ara nvarchar(max)
AS
BEGIN
DECLARE #sql NVARCHAR(MAX);
DECLARE #NAME VARCHAR(100);
SET #sql = '
Select STOK_KODU,STOK_ADI,GRUP_KODU from TBLSTSABIT where 1=1'
DECLARE CUR CURSOR FOR
SELECT Deger FROM dbo.splitstring(''+#Ara+'')
OPEN CUR
FETCH NEXT FROM CUR INTO #NAME
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql=#sql+' AND STOK_ADI+STOK_KODU+GRUP_KODU LIKE ''%'+#NAME+'%'''
FETCH NEXT FROM CUR INTO #NAME
END
CLOSE CUR
DEALLOCATE CUR
SET #sql = #sql + ' order by STOK_KODU asc offset (CAST('+#PageIndex+' as int)*CAST('+#PageSize+' as int)) Rows fetch next CAST('+#PageSize+' as int) rows only
'
EXEC sp_executesql #sql;
PRINT #SQL;
RETURN
END
on the C# side like this
StringBuilder test = new StringBuilder();
JsonModel jsonmodel = new JsonModel();
if (Arama != "")
{
foreach (var item in Arama.Split(' '))
{
test.Append(item + "~");
}
}
test.Remove(test.ToString().Length - 1, 1);
jsonmodel.Filtre = test.ToString();
int pagesize = 15;
var tbrow = isStatic.GetStokListesi(pageindex, pagesize, Arama);
jsonmodel.NoMoredata = tbrow.Count < pagesize;
jsonmodel.HTMLString = isStatic.RenderToString(PartialView("_partial", tbrow));
return Json(jsonmodel);
This is the search function.
public static List<Stoklar> GetStokListesi(int pageindex, int pagesize, string Arama)
{
using (blabla db = new blabla())
{
return db.Database.SqlQuery<Stoklar>("[GetStoklar] #PageIndex,#PageSize,#Ara",
new SqlParameter("#PageIndex", pageindex.ToString()), new SqlParameter("#PageSize", pagesize.ToString()), new SqlParameter("#Ara", Arama)).ToList();
}
}
Is there a way to use this more meaningfully on the SQL side?
You can replace all single characters in a word by an '_', and then use these results in a LIKE
For replacing all single characters in a word, see: DBFIDDLE

Export database table to existing excel file

I've looked already but what I found so far is exporting to a existing excel sheet, what I need is to make a copy of an existing excel template I have and pass my data there. I just need to be pointed in the right way, hope I'm clear enough.
EDIT
I have an application working on Java to which I will add this.
Assuming that you ask about SQL Server, you can create 2 step job, one for copying your xls, and in second step using openrowset/insert into your copied excel file.
Here is Tsql which copies template and also sends an email, and info about xp_cmdshell https://msdn.microsoft.com/en-us/library/ms190693.aspx
DECLARE #cmd varchar(512)
DECLARE #fd varchar(512)
DECLARE #odbc varchar(128)
DECLARE #db varchar(128)
SELECT #fd = 'D:\Reports\Exact\ByWeek' + CONVERT(VARCHAR(10), GETDATE()-8,120)+'_'+CONVERT(VARCHAR(10), GETDATE(),120)+'.xls'
SELECT #cmd = 'copy D:\Reports\System\shipments_week.xls ' + #fd
EXEC MASTER..XP_CMDSHELL #cmd, NO_OUTPUT
SET #odbc = 'Microsoft.Jet.OLEDB.4.0'
SET #db = 'Excel 8.0;Database=' + #fd
exec('INSERT INTO OPENrowset(''' + #odbc + ''',''' + #db + ''',''SELECT
OrderNr,Debtor,SUM_NSHIPPED,SUM_TOTAL,PERC_NSHIPPED,ORDDAT,INVDAT,ORD_SYSCREATED,HIST_LAST_MODIFIED,PT_WZ_SENT
FROM [Shipped$]'')
SELECT * FROM salag_shipmentsbyWeek')
exec('INSERT INTO OPENrowset(''' + #odbc + ''',''' + #db + ''',''SELECT
OrderNr,Debtor,SUM_NSHIPPED,SUM_TOTAL,PERC_NSHIPPED,ORDDAT,INVDAT,ORD_SYSCREATED,HIST_LAST_MODIFIED,PT_WZ_SENT
FROM [SB$]'')
SELECT * FROM salag_shipmentsbyWeek WHERE Debtor NOT LIKE ''%CASTORAMA%'' AND Debtor NOT LIKE ''Praktiker%''')
DECLARE #Body VARCHAR(4096)
DECLARE #BodyType VARCHAR(16)
DECLARE #path VARCHAR(128)
DECLARE #f VARCHAR(32)
DECLARE #topic VARCHAR(128)
SELECT #f = CONVERT(VARCHAR(10), GETDATE()-8,120)+'_'+CONVERT(VARCHAR(10), GETDATE(),120)
SELECT #topic = 'New Report ['+ CONVERT(VARCHAR(10), GETDATE()-8,120)+'_'+CONVERT(VARCHAR(10), GETDATE(),120) + ']'
SELECT #path = '\\appsrv\Reports\Exact\ByWeek' + CONVERT(VARCHAR(10), GETDATE()-8,120)+'_'+CONVERT(VARCHAR(10), GETDATE(),120)+'.xls'
SELECT #Body = 'New Scan Report has been created: '+#f+' '+ #path
SELECT #BodyType = 'HTMLBody'
exec sp_send_cdosysmail 'Reports Info ','reports#foo ar.com',#topic, #Body, #BodyType
I was able to achieve what I was looking for using JExcel and it was pretty easy, here I will share my code and hope it helps someone in the future. Sorry its not properly commented yet, I will later post an update.
Db_Connect connection = new Db_Connect();
conn = connection.connect();
String originalFile = "C:\\Users\\Geni\\Desktop\\Book1-Template-new.xls";
date = date.replace("/", "-");
SimpleDateFormat myDate = new SimpleDateFormat("yyyy-MM-dd");
try{
Workbook original = Workbook.getWorkbook(new File(originalFile));
WritableWorkbook copy = Workbook.createWorkbook(new File(date+".xls"), original);
java.util.Date format = myDate.parse(date);
java.sql.Date newDate = new java.sql.Date(format.getTime());
String sql = "SELECT Name, sporecount.* FROM sporesfungi "
+ "INNER JOIN sporecount ON sporesfungi.IDSpore = sporecount.IDSpore"
+ "WHERE Date = ? ORDER BY TraceNum";
PreparedStatement statement = conn.prepareStatement(sql);
statement.setDate(1, newDate);
rs = statement.executeQuery();
/*stmt = conn.createStatement();
rs = stmt.executeQuery(sql);*/
WritableSheet sheet = copy.getSheet(0);
WritableCell cell;
String spore;
while(rs.next()){
for(int i = 2; i < 64 ;i++){
cell = sheet.getWritableCell(1,i);
spore = cell.getContents();
if(rs.getString("Name").equals(spore)){
Number l1 = new Number(14-rs.getInt("TraceNum"),i,rs.getInt("Amount")) ;
sheet.addCell(l1);
}
}
}
copy.write();
copy.close();
original.close();
}
catch (BiffException | IOException e) {
}

How to create RDLC report from dynamic query in SQL Server Express

I can't create the rdlc report from this procedure, I'm using report viewer but when I select the procedure, it doesn't show any column names but my procedure returns 7 columns. How can I create the report please help ? I'm creating the report in vb.net
ALTER PROCEDURE Pr_getAcceptedOnDateReport
#date date
AS
SET FMTONLY OFF
DECLARE #SQL varchar(MAX)
DECLARE #antiHIV bit
DECLARE #HBsAg bit
DECLARE #IGMHBCore bit
DECLARE #NAT bit
DECLARE #Malaria bit
DECLARE #TotalHBCore bit
DECLARE #Syphilis bit
DECLARE #HCV bit
DECLARE #ICT bit
DECLARE #DCT bit
DECLARE #Antibody bit
Select #antiHIV=[HIV1/2 screen Reactive],
#IGMHBCore=[IgM HBcore Reactive],
#HBsAg=[HBsAg Screen Reactive],
#NAT= [NAT Reactive],
#Malaria=[Malaria Screen Reactive],
#TotalHBCore=[Total HBcore Reactive],
#Syphilis=[Syphilis Screen Reactive],
#HCV=[HCV screen Reactive],
#ICT=[ICT Positive],
#DCT= [DCT Positive],
#Antibody= [Antibody Screen Positive]
from m_rejectionRules where deleted=0
DECLARE #sql1 nvarchar(4000)
Select #sql1='Select t.donorid, t.donorname, t.sampleid, t.customid,t.bagtype,t.bagnumber, t.segmentno from ttidetail t, m_donor m
where t.donorid=m.donorid
AND CONVERT(date, m.RDate) =''' + cast(#date as varchar(100)) + ''''
IF #antiHIV='True'
BEGIN
SELECT #sql1 = #sql1 + ' AND t.antiHIV like ' + ''''+ 'Non%Reactive'+''''
END
IF #HBsAg='True'
BEGIN
SELECT #sql1 = #sql1 + ' AND t.HBsAg like '+ ''''+ 'Non%Reactive'+''''
END
IF #IGMHBCore='True'
BEGIN
SELECT #sql1 = #sql1 + ' AND t.IGM_antiHBC like '+ ''''+ 'Non%Reactive'+''''
END
IF #NAT='True'
BEGIN
SELECT #sql1 = #sql1 + ' AND t.NAT_HIV1 like '+ ''''+ 'Non%Reactive'+''''
END
IF #Malaria='True'
BEGIN
SELECT #sql1 = #sql1 + ' AND t.malariaScreen like '+ ''''+ 'Non%Reactive'+''''
END
IF #TotalHBCore='True'
BEGIN
SELECT #sql1 = #sql1 + ' AND t.totalAnti_HBC like '+ ''''+ 'Non%Reactive'+''''
END
IF #Syphilis='True'
BEGIN
SELECT #sql1 = #sql1 + ' AND t.SyphilisScreen like '+ ''''+ 'Non%Reactive'+''''
END
EXEC sp_executesql #sql1
Create a class which maps to your fields returned by your stored procedure
Public Class ReportData
Property donorid AS Integer = 0
Property donorname as string = string.empty
Property sampleid as integer = 0
Property customid as integer = 0
Property bagtype as string = string.empty
Property bagnumber as integer = 0
Property segmentno as integer = 0
End Class
Assuming that you have a function which returns a dataset using the above query, I will refer to it as ds
* MOST IMPORTANT *
When creating your rdlc report instead of mapping to the storedprocdure map it to an object datasource and select the class you just created. Use the fileds in this class as fields for your report.
In the button show report you can use the following code to display your report
Private Sub ShowReport()
Dim dsL As DataSet = New DataSet()
dsL = GetReportData() ' Function which will get the data from the SQL Query
Dim rds As ReportDataSource = New ReportDataSource()
rds.Name = "ReportDS" ' Change to what you will be using when creating an objectdatasource
rds.Value = dsL.Tables(0)
With rvReport ' Name of the report control on the form
.Reset()
.ProcessingMode = ProcessingMode.Local
.LocalReport.DataSources.Clear()
.Visible = True
.LocalReport.ReportPath = reportPath
.LocalReport.DataSources.Add(rds)
' If you have any parameters you can pass them here
Dim rptParameters As New List(Of ReportParameter)()
With rptParameters
.Add(New ReportParameter("Title",
String.Format("{0} Grid For Period {1} To {2}",
gridType,
FormatDateTime(startDate, DateFormat.ShortDate),
FormatDateTime(endDate, DateFormat.ShortDate))))
.Add(New ReportParameter("SubTitle", String.Format("Program: {0}", ucProgram.Text)))
.Add(New ReportParameter("StartDate", startDate))
.Add(New ReportParameter("EndDate", endDate))
End With
.LocalReport.SetParameters(rptParameters)
.RefreshReport()
End With
End Sub

Writing a dreaded SQL search query (2nd phase)

I am working on a search query (with an asp.net 3.5 front end) which seems quite simple, but is quite complex.
The complete query is:
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
ALTER PROCEDURE [dbo].[usp_Item_Search]
#Item_Num varchar(30) = NULL
,#Search_Type int = NULL
,#Vendor_Num varchar(10) = NULL
,#Search_User_ID int = 0
,#StartDate smalldatetime = NULL
,#EndDate smalldatetime = NULL
AS
DECLARE #SQLstr as nvarchar(4000)
Set #SQLstr = 'SELECT RecID, Vendor_Num, Vendor_Name, InvoiceNum, Item_Num,
(SELECT CONVERT(VARCHAR(11), RecDate, 106) AS [DD MON YYYY]) As RecDate, NeedsUpdate, RecAddUserID FROM [tbl_ItemLog] where 1=1 '
IF (#Item_Num IS NOT NULL and LTRIM(#Item_Num) <> '')
Begin
If #Search_Type = 0
BEGIN
Set #SQLstr = #SQLstr + 'AND Item_Num LIKE ''' + #Item_Num + '%'''
END
If #Search_Type = 1
BEGIN
Set #SQLstr = #SQLstr + 'AND Item_Num LIKE ''%' + #Item_Num + '%'''
END
If #Search_Type = 2
BEGIN
Set #SQLstr = #SQLstr + 'AND Item_Num LIKE ''%' + #Item_Num + ''''
END
End
IF (#Vendor_Num IS NOT NULL and LTRIM(#Vendor_Num) <> '')
Begin
Set #SQLstr = #SQLstr + ' AND Vendor_Num = ''' + #Vendor_Num + ''''
End
IF (#Search_User_ID IS NOT NULL and #Search_User_ID > 0)
Begin
Set #SQLstr = #SQLstr + ' AND RecAddUserID = ' + convert(nvarchar(20),#Search_User_ID)
End
Set #SQLstr = #SQLstr + ' AND (RecDate BETWEEN ''' + convert(nvarchar(10),#StartDate,106) + ''' AND ''' + convert(nvarchar(10),#EndDate,106) + ''')'
PRINT (#SQLstr)
--Execute (#SQLstr)
When I pass all empty parameter values, I get an error:
"Failed to convert parameter value
from a String to a Int32."
The asp.net code that is calling the stored proc is:
//Display search results in GridView;
SqlConnection con = new SqlConnection(strConn);
//string sqlItemSearch = "usp_Item_Search";
SqlCommand cmdItemSearch = new SqlCommand(sqlItemSearch, con);
cmdItemSearch.CommandType = CommandType.StoredProcedure;
cmdItemSearch.Parameters.Add(new SqlParameter("#Item_Num", SqlDbType.VarChar, 30));
cmdItemSearch.Parameters["#Item_Num"].Value = txtItemNumber.Text.Trim();
cmdItemSearch.Parameters.Add(new SqlParameter("#Search_Type", SqlDbType.Int));
cmdItemSearch.Parameters["#Search_Type"].Value = ddlSearchType.SelectedItem.Value;
cmdItemSearch.Parameters.Add(new SqlParameter("#Vendor_Num", SqlDbType.VarChar, 10));
cmdItemSearch.Parameters["#Vendor_Num"].Value = txtVendorNumber.Text.Trim();
cmdItemSearch.Parameters.Add(new SqlParameter("#Search_User_ID", SqlDbType.Int));
cmdItemSearch.Parameters["#Search_User_ID"].Value = ddlSeachUser.SelectedItem.Value;
if (!string.IsNullOrEmpty(txtStartDate.Text))
{
cmdItemSearch.Parameters.Add(new SqlParameter("#StartDate", SqlDbType.DateTime));
cmdItemSearch.Parameters["#StartDate"].Value = Convert.ToDateTime(txtStartDate.Text.Trim());
}
else
{
cmdItemSearch.Parameters.Add(new SqlParameter("#StartDate", SqlDbType.DateTime));
cmdItemSearch.Parameters["#StartDate"].Value = Convert.ToDateTime("01/01/1996");
}
if (!string.IsNullOrEmpty(txtEndDate.Text))
{
cmdItemSearch.Parameters.Add(new SqlParameter("#EndDate", SqlDbType.DateTime));
cmdItemSearch.Parameters["#EndDate"].Value = Convert.ToDateTime(txtEndDate.Text.Trim());
}
else
{
cmdItemSearch.Parameters.Add(new SqlParameter("#EndDate", SqlDbType.DateTime));
cmdItemSearch.Parameters["#EndDate"].Value = Convert.ToDateTime(DateTime.Now);
}
con.Open();
SqlDataAdapter ada = new SqlDataAdapter(cmdItemSearch);
DataSet ds = new DataSet();
ada.Fill(ds);
gvSearchDetailResults.DataSource = ds;
gvSearchDetailResults.DataBind();
pnlSearchResults.Visible = true;
How can I resolve this?
You're not quite building the string correctly as far as I can tell. If no #Item_Num is passed in, you'll end up with no WHERE key word... you'll just have "FROM [tblItem_Log] AND..."
I would make all of the criteria appends be "AND ..." and as your initial statement use:
FROM [tbl_Item_Log] WHERE (1=1)
Since you have code to return the generated string, why not put that into SSMS and try to run it?
I also just noticed that if you don't pass in date values that you will end up executing a NULL string, because your final concatenation will end up causing a NULL. These are the kinds of things that you need to pay very close attention to if you're going to be using dynamic SQL to build queries.
Once I corrected that I was able to run the stored procedure without any errors (at least to generate what looks like a valid SQL statement). That leads me to believe that it may be a problem with data types in the underlying table. Can you provide the definition for that?
One last note: Personally, I would use
CONVERT(VARCHAR(11), RecDate, 106) AS RecDate
instead of the seemingly unnecessary subquery that you have.
Yet another edit:
You may want to remove the code that checks LTRIM(#Search_User_ID) <> ''. It's a pointless bit of code and perhaps a setting particular to your server/connection is causing it to fail because of the type mismatch.
IF (Search_User_ID IS NOT NULL)
needs an # symbol infront of the variable
You say you are passing empty string in for all variables but one is an int, it can't take an empty string that is not int data. Can't believe I didn;t notice that the first time.
Why don't you use single parameterized query like this:
select
recdid,
Vendor_Num,
Vendor_Name,
InvoiceNum,
Item_Num,
CONVERT(VARCHAR(11), RecDate, 106) as RecDate,
NeedsUpdate,
RecAddUserID
FROM
[tbl_ItemLog] as t
where
(((Item_num like #Item_Num + '%' and #Search_Type = 0) OR
(Item_num like '%' + #Item_Num + '%' and #Search_Type = 1) OR
(Item_num like '%' + #Item_Num + '%' and #Search_Type = 2))
OR
#Item_Num IS NULL) AND
(Vendor_Num = #Vendor_Num OR #Vendor_Num IS NULL) AND
(RecAddUserId = #Search_User_Id OR #Search_User_Id IS NULL) AND
(RecDate BETWEEN #StartDate AND #EndDate)
You really have several different stored procedures here. Why not just write them separately? Everything that's controlled by switch statements could be easily in procedural code. Same for the LTRIM calls.
You could call them all from a single SP with switch statements; but I think it's generally better to not merge them in the first place. The SP queries will optimize more easily, and the code will be simpified. There's not much you gain by consolidating them.
Not sure of your business rules, but you could simplify this outside SQL with
switch(search_type) {
case 1:
do_query_type_1(args);
break;
case 2:
do_query_type_2(args);
break;
case 3:
do_query_type_3(args);
break;
default:
whatever ...
}
Also it looks like you have separate logic for cases where the item number is provided or not. Same for other fields. Each of your use cases looks like it resolves to a pretty simple query.

Parameterizing Dynamic SQL issue with SQL_VARIANT datatype

I've been refactoring a huge dynamic SQL statement, and think I may have hit a roadblock. The goal has been to parameterize it. I'm using SQL Server 2005.
I have an int variable (#i) that determines what column I need to update. Here's what I've been doing:
if #i = 1
begin
set #updateClause = 'Column1 = #newValue';
set #updateClauseDataType = 'varchar(10)';
set #updateValue = #foo;
end
else if #i = 2
begin
set #updateClause = 'Column2 = #newValue';
set #updateClauseDataType = 'int';
set #updateValue = #bar;
end
else if ...
Then, I try to execute my statement:
set #statement = N'update ' + #server_name + '.' + #database_name + '.dbo.Table1
set ' + #updateClause + ' where pk = #pk';
set #parameters = '#newValue ' + #updateClauseDataType + ',
#pk uniqueidentifier';
execute sp_executesql #statement,
#parameters,
#newValue = #newValue,
#pk= #pk;
This results in:
Implicit conversion from data type
sql_variant to varchar is not allowed.
Use the CONVERT function to run this
query.
I can get around this if I can somehow cast #updateValue into the correct type, which I have stored as text in #updateClauseDataType. Is there any way I can do this without having a huge if statement that interrogates the value of #updateClauseDataType?
On rereading, since you already know the data type, why not include it in the update clause?
set #updateClause = 'Column1 = cast(#newValue as varchar(10))';
set #updateClauseDataType = 'varchar(10)';
Or to avoid typing the data type twice:
set #updateClauseDataType = 'varchar(10)';
set #updateClause = 'Column1 = cast(#newValue as ' +
#updateClauseDataType + ')';