Pass DECLARE parameters in SQL query - c#

I have the following code:
SqlCommand command = new SqlCommand(
#"DECLARE #month int, #year int, #dateToCheck datetime;
SET #month = #month;
SET #year = #year;
SET #dateToCheck = dateadd(month, 1, datefromparts(#year, #month, 1))
SELECT p.name, dtc.cost_price, p.date_created
FROM [dbo].[Company_Local_Invoice_] claidig
JOIN Type_Company dtc on claidig.ID = dtc.id
WHERE p.date_created < #dateToCheck
AND (p.date_left is null or p.date_left >= #dateToCheck)", conn);
command.Parameters.Add("#month", SqlDbType.Int).Value = month;
command.Parameters.Add("#year", SqlDbType.Int).Value = year;
The problem is that I can't seem to pass my SET parameters using command.Parameter.Add() .
The error that I get is:
The variable name '#month' has already been declared. Variable names must be unique within a query batch or stored procedure.
Why is this and how can I work around this?

The point Gordon is making is that when you pass parameters to a sql string, it prepends the 'declare' statements from the parameter definitions. So, you don't need to do the declare for anything that's coming in as parameters. You still need to declare any variable that gets computed from the parameters though.
var commandText = #"
declare #dateToCheck datetime
set #dateToCheck = dateadd(month, 1, datefromparts(#year, #month, 1))
select
p.name, dtc.cost_price, p.date_created
from
dbo.[Company_Local_Invoice_] claidig
inner join
Type_Company dtc
on c
laidig.ID = dtc.id
where
p.date_created < #dateToCheck
and
(
p.date_left is null
or
p.date_left >= #dateToCheck
)";
var command = new SqlCommand(commandText, conn);
command.Parameters.Add("#month", SqlDbType.Int).Value = month;
command.Parameters.Add("#year", SqlDbType.Int).Value = year;

Just pass in the parameters and do the calculations in the query:
SELECT p.name, dtc.cost_price, p.date_created
FROM [dbo].[Company_Local_Invoice_] claidig
JOIN Type_Company dtc ON claidig.ID = dtc.id
CROSS APPLY (VALUES
(dateadd(month, 1, datefromparts(#year, #month, 1)))
) v(dateToCheck)
WHERE p.date_created < v.dateToCheck AND
(p.date_left is null or p.date_left >= v.dateToCheck);

Related

Date Format: No overload for method "ToString" takes 1 arguments

I stay with that error when I'm trying to format a date in my code:
Cmd.CommandText = #"
DECLARE #command varchar(5000);
DECLARE #RestoreList TABLE(DB_name VARCHAR(100), RS_name VARCHAR(100), RS_DateFinExercice DATE, RS_IsClosed VARCHAR(50));
SELECT #command = 'IF ''?'' IN (SELECT name FROM sys.databases WHERE HAS_DBACCESS(name) = 1 AND CASE WHEN state_desc = ''ONLINE'' THEN OBJECT_ID( QUOTENAME( name ) + ''.[dbo].[P_DOSSIER]'',''U'' ) END IS NOT NULL) BEGIN USE [?] SELECT DB_name = CAST(DB_NAME() AS VARCHAR(100)), RS_name = CAST(a.D_RaisonSoc AS VARCHAR(100)), RS_DateFinExercice = CAST((SELECT Max(v) FROM (VALUES (a.[D_FinExo01]), (a.[D_FinExo02]), (a.[D_FinExo03]),(a.[D_FinExo04]),(a.[D_FinExo05])) AS value(v)) AS DATE), RS_IsClosed = CAST((SELECT CASE WHEN (SUM (CASE WHEN JM_Cloture !=2 THEN 1 ELSE 0 END)>0) THEN '''' ELSE ''arc'' END FROM F_JMOUV) AS VARCHAR(50)) FROM [dbo].[P_DOSSIER] a INNER JOIN F_JMOUV b ON DB_name() = DB_NAME() GROUP BY D_RaisonSoc, D_FinExo01, D_FinExo02, D_FinExo03, D_FinExo04, D_FinExo05 HAVING COUNT(*) > 1 END'
INSERT INTO #RestoreList EXEC sp_MSforeachdb #command;
SELECT * FROM #RestoreList ORDER BY DB_name;";
SqlDataReader dr = Cmd.ExecuteReader();
List<DBtoRestore> dgUIDcollection = new List<DBtoRestore>();
if (dr.HasRows)
{
while (dr.Read())
{
DBtoRestore currentdgUID = new DBtoRestore
{
CUID_dbname = dr["DB_name"].ToString(),
CUID_RaisonSoc = dr["RS_name"].ToString(),
CUID_DateFinExercice = dr["RS_DateFinExercice"].ToString(),
CUID_IsClosed = dr["RS_IsClosed"].ToString()
};
dgUIDcollection.Add(currentdgUID);
}
}
dgDBtoRestore.ItemsSource = dgUIDcollection;
Cnx.Close();
The problem is on this line of code:
CUID_DateFinExercice = dr["RS_DateFinExercice"].ToString()
For now, my datagrid report date like 01/01/2020 00:00:00. In SQL, I have 01-01-2020 style.
I want to have the same style in my datagrid.
I have try something like ToString("dd-MM-yyyy") but it's in that context I've received the error.
Any idea to help me?
Convert to a DateTime and then call ToString on it:
Convert.ToDateTime(dr["RS_DateFinExercice"]).ToString("dd-MM-yyyy")
Solution :
CUID_DateFinExercice = ((DateTime)dr["RS_DateFinExercice"]).ToString("dd-MM-yyyy"),

Getting weird value in C#

Getting exactly correct data in stored procedure. but when i am trying to get that value in C# datatable getting wrong data. not sure why i am getting that.
Here is my stored procedure:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[GetSurveyStatistic]
(#SurveyID int,
#NameOfSubmitter varchar(200),
#NameOfPrivacyContact varchar(200),
#HspOrganizationalName varchar(200),
#HspSiteNumber varchar(200),
#FromDate datetime,
#ToDate datetime,
#weekly bit)
AS
BEGIN
IF (#ToDate IS NOT NULL)
SET #ToDate = DATEADD(DAY, 1, #ToDate)
IF #Weekly = 0
BEGIN
SELECT
CAST(CAST(StartedDateTime AS date) AS varchar(10)) Value,
COUNT(*) cnt
FROM
SubmittedSurveys
WHERE
(SurveyID = #SurveyID OR #SurveyID IS NULL)
AND (StartedDateTime >= #FromDate OR #FromDate IS NULL)
AND (StartedDateTime <= #ToDate OR #ToDate IS NULL)
AND (LOWER(ProvidedNameOfSubmitter) LIKE LOWER(#NameOfSubmitter) + '%'
OR #NameOfSubmitter IS NULL OR #NameOfSubmitter = '')
AND (LOWER(NameOfPrivacyContact) LIKE LOWER(#NameOfPrivacyContact) + '%'
OR #NameOfPrivacyContact IS NULL
OR #NameOfPrivacyContact = '')
AND (LOWER(HspOrganizationalName) LIKE LOWER(#HspOrganizationalName) + '%'
OR #HspOrganizationalName IS NULL
OR #HspOrganizationalName = '')
AND (LOWER(HspSiteNumber) LIKE LOWER(#HspSiteNumber)
OR #HspSiteNumber IS NULL OR #HspSiteNumber = '')
GROUP BY
CAST(StartedDateTime AS date)
ORDER BY
1
END
ELSE BEGIN
SELECT
CAST(DATEPART(WEEK, StartedDateTime) AS varchar(10)) Value,
COUNT(*) cnt
FROM
SubmittedSurveys
WHERE
(SurveyID = #SurveyID OR #SurveyID IS NULL)
AND (StartedDateTime >= #FromDate OR #FromDate IS NULL)
AND (StartedDateTime <= #ToDate OR #ToDate IS NULL)
AND (LOWER(ProvidedNameOfSubmitter) LIKE LOWER(#NameOfSubmitter) + '%'
OR #NameOfSubmitter IS NULL OR #NameOfSubmitter = '')
AND (LOWER(NameOfPrivacyContact) LIKE LOWER(#NameOfPrivacyContact) + '%'
OR #NameOfPrivacyContact IS NULL
OR #NameOfPrivacyContact = '')
AND (LOWER(HspOrganizationalName) LIKE LOWER(#HspOrganizationalName) + '%'
OR #HspOrganizationalName IS NULL
OR #HspOrganizationalName = '')
AND (LOWER(HspSiteNumber) LIKE LOWER(#HspSiteNumber)
OR #HspSiteNumber IS NULL OR #HspSiteNumber = '')
GROUP BY
CAST(DATEPART(WEEK, StartedDateTime) AS varchar(10))
ORDER BY
1
END
END
and here is the results
but in front end getting "Value" ="Date" instead of week value
Here is the code of front end
public static List<SubmittedSurveys> GetSurveyStatistic(int SubmittedSurveyId, DateTime
StartedDateTime, string NameOfSubmitter, string NameOfPrivacyContact, string
HspOrganizationalName, string HspSiteNumber,
DateTime CompletedDateTime,bool weekly)
{
try
{
DatabaseProviderFactory factory = new DatabaseProviderFactory();
Database _wohcDB = factory.Create("SurveyToolDBEntities");
// SqlDatabase _wohcDB = DatabaseFactory.CreateDatabase("SurveyToolDBEntities") as
SqlDatabase;
string sqlCommand = "[GetSurveyStatistic]";
DbCommand dbCommand = _wohcDB.GetStoredProcCommand(sqlCommand);
_wohcDB.AddInParameter(dbCommand, "#SurveyID", DbType.Int32, SubmittedSurveyId);
_wohcDB.AddInParameter(dbCommand, "#FromDate", DbType.DateTime, StartedDateTime);
_wohcDB.AddInParameter(dbCommand, "#ToDate", DbType.DateTime, CompletedDateTime);
_wohcDB.AddInParameter(dbCommand, "#NameOfSubmitter", DbType.String, NameOfSubmitter);
_wohcDB.AddInParameter(dbCommand, "#NameOfPrivacyContact", DbType.String, NameOfPrivacyContact);
_wohcDB.AddInParameter(dbCommand, "#HspOrganizationalName", DbType.String, HspOrganizationalName);
_wohcDB.AddInParameter(dbCommand, "#HspSiteNumber", DbType.String, HspSiteNumber);
_wohcDB.AddInParameter(dbCommand, "#weekly", DbType.String, weekly);
DataSet ds = _wohcDB.ExecuteDataSet(dbCommand);
_submittedSurvey = ReportsController.DbConverter.DataTable2List<SubmittedSurveys>(ds.Tables[0]);
//_submittedSurvey[0].SurveyReports = ReportsController.DbConverter.DataTable2List<Reports>(ds.Tables[1]);
}
catch (Exception ex)
{
string message = ex.Message.ToString();
}
return _submittedSurvey;
}
Here is the screenshot of front end where I am getting wrong value as a date instead of week
Can somebody help me to resolve this issue. This is so weird. i also tried to cast in int but getting error that date can not convert into int. I am not sure that why I am getting wrong data in front end and getting right data in backend?
Please help me.
Try to Change:
_wohcDB.AddInParameter(dbCommand, "#weekly", DbType.String, weekly);
To:
_wohcDB.AddInParameter(dbCommand, "#weekly", DbType.Boolean, weekly);
Just run the else part then it will work fine. with this query it will always go to if statement because of #Weekly =0.

How to execute Anonymous Block PL/pgSQL (PostgreSQL 13) from Npgsql 4.1.5.0 in C#

I have this anonymous block PL/pgSQL:
DO
$$
DECLARE secuencial INT;
BEGIN
SELECT MAX("CodigoFactura") + 1 INTO secuencial FROM "Factura";
IF secuencial IS NULL THEN
secuencial := 1;
END IF;
RAISE NOTICE '%', secuencial;
END;
$$
The anonymous block PL/pgSQL execute from Npgsql like this:
NpgsqlConnection npgsqlConnection = new NpgsqlConnection("Server=127.0.0.1;Port=5432;Database=myBase;User Id=user;Password=password;");
npgsqlConnection.Open();
string sentencialSQL = "DO $$ BEGIN SELECT MAX(\"CodigoFactura\") + 1 INTO :v_secuencial FROM \"Factura\"; IF :v_secuencial is NULL THEN :v_secuencial := 1; END IF; END; $$";
NpgsqlCommand npgsqlCommand = new NpgsqlCommand(sentencialSQL, npgsqlConnection);
// ===============================
NpgsqlParameter npgsqlParameter1 = new NpgsqlParameter();
npgsqlParameter1.ParameterName = ":v_secuencial";
npgsqlParameter1.Value = 0;
npgsqlParameter1.NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Integer;
npgsqlParameter1.Direction = ParameterDirection.Output;
npgsqlCommand.Parameters.Add(npgsqlParameter1);
// ===============================
npgsqlCommand.CommandType = CommandType.Text;
npgsqlCommand.ExecuteNonQuery();
npgsqlConnection.Close();
And I have this error:
42601: syntax error at or near <<:>>
The statement DO is server side statement that doesn't support parametrisation. You cannot pass any parameters to DO block directly. For this case you should to write a function or just use a COALESCE function:
SELECT COALESCE(MAX("CodigoFactura") + 1, 1) INTO secuencial FROM "Factura";
Attention - using case sensitive identifiers in SQL is pretty bad pattern (very impractical).
According #Pavel Stehule this is the solution (if someone need a complete solution):
NpgsqlConnection npgsqlConnection = new NpgsqlConnection("Server=127.0.0.1;Port=5432;Database=myBase;User Id=user;Password=password;");
npgsqlConnection.Open();
string sentencialSQL = "SELECT COALESCE(MAX(\"CodigoFactura\") + 1, 1) FROM \"Factura\";";
NpgsqlCommand npgsqlCommand = new NpgsqlCommand(sentencialSQL, npgsqlConnection);
// ===============================
npgsqlCommand.CommandType = CommandType.Text;
object secuencial = npgsqlCommand.ExecuteScalar();
npgsqlConnection.Close();

Must declare the scalar variable "#ManagerID"

I have a Running Time Error:
Must declare the scalar variable \"#ManagerID
I'm Sure I Have Declare All Variables In My CLass And My Procudure
My Class Code:
public DataTable Select(int ID,string NameFa, string Address, int ManagerID, short TotalUnits, int ChargeMethodID)
{
DataTable table = new DataTable();
table.Columns.Add("ID", typeof(int));
table.Columns.Add("NameFa", typeof(string));
table.Columns.Add("Address", typeof(string));
table.Columns.Add("ManagerID", typeof(int));
table.Columns.Add("TotalUnits", typeof(short));
table.Columns.Add("ChargeMethodID", typeof(int));
try
{
con.Open();
SqlCommand command = new SqlCommand("dbo.SelectBuilding", con);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("#ID", ID));
command.Parameters.Add(new SqlParameter("#NameFa", NameFa));
command.Parameters.Add(new SqlParameter("#Address", Address));
command.Parameters.Add(new SqlParameter("#ManagerID", ManagerID));
command.Parameters.Add(new SqlParameter("#TotalUnits", TotalUnits));
command.Parameters.Add(new SqlParameter("#ChargeMethodID", ChargeMethodID));
SqlDataAdapter adapter = new SqlDataAdapter(command);
adapter.Fill(table);
return table;
}
And My Procudure Code Is:
#ID int,
#NameFa nvarchar(150),
#Address nvarchar(MAX),
#ManagerID int,
#TotalUnits smallint,
#ChargeMethodID int
As
Begin
IF(#ID >0 )
Begin
Select ID,NameFa,Address,ManagerID,TotalUnits,ChargeMethodID From Buildings where ID = #ID
End
ELSE
Begin
Declare #sqlTxt nvarchar(MAX)
SET #sqlTxt = 'SELECT ID,NameFa,Address,ManagerID,TotalUnits,ChargeMethodID FROM Buildings where ID>0'
IF(#NameFa!= null)
BEGIN
SET #sqlTxt = #sqlTxt + ' AND NameFa Like ''%#NameFa%'''
END
IF(#Address!= null)
BEGIN
SET #sqlTxt = #sqlTxt + ' AND Address Like ''%#Address%'''
END
IF(#ManagerID > 0)
BEGIN
SET #sqlTxt = #sqlTxt + ' AND ManagerID = #ManagerID'
END
IF(#TotalUnits > 0)
BEGIN
SET #sqlTxt = #sqlTxt + ' AND TotalUnits = #TotalUnits'
END
IF(#ChargeMethodID > 0)
BEGIN
SET #sqlTxt = #sqlTxt + ' AND ChargeMethodID = #ChargeMethodID'
END
EXEC (#sqlTxt);
End
END
And I want to use Select Function:
DataTable dt = new DataTable();
Buildings.Building bb = new Buildings.Building() {ID=0,NameFa="",Address="",ManagerID=OwnerID,TotalUnits=0,ChargeMethodID=0 };
dt = bu.Select(bb.ID,bb.NameFa,bb.Address,bb.ManagerID,bb.TotalUnits,bb.ChargeMethodID);
You are not passing the parameters to the exec statement. I would change it to sp_executesql which has an optional argument with parameters.
https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-executesql-transact-sql
Edit: I strongly suggest getting rid of the exec and/or sp_executesql commands. Because depending on the input you could:
a) Get runtime errors due to user typing SQL string delimiters as a valid input. Example O'Hara as a surname.
b) A malicious user could mess badly with your database.
You could get similar result in a more simple way:
Select
ID,NameFa,Address,ManagerID,TotalUnits,ChargeMethodID
From
Buildings
where
(#Id = 0 or ID = #Id)
and (#NameFa = '' or NameFa = #NameFa)
and (#ManagerID = 0 or ManagerID = #ManagerID)
// repeat for the rest of the optional search conditions

How to get table name from string that contain SELECT statement

i have a string that contain a sql command,
something like this:
strCommand = "Select [Feild1],
[Feild2]
From TableName
Order By [Feild1] desc" ;
How can find table name in this string?
The solutions so far have all gone with the searching within strings approach. You've not mentioned if your SQL queries will always look similar, but there are many variants of a query to include which these solutions will break on. Consider...
SELECT Field1, Field2 FROM TableName
SELECT Field1, Field2 FROM [TableName]
SELECT Field1, Field2 FROM dbo.TableName
SELECT Field1, Field2 FROM Table1Name, Table2Name
If the query you're trying to parse is one you have the database for, you can get SQL server to do the hard work of parsing the query for you, instead of trying to account for all the cases in SQL. You can execute a query using SET SHOWPLAN_ALL ON, which will produce a table of the query plan. You can then analyse the Arguments column, which contains all of the fields the query will involve in a standard format. An example program is below:
SqlConnection conn = new SqlConnection(CONNECTIONSTRING);
conn.Open();
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "SET SHOWPLAN_ALL ON";
cmd.ExecuteNonQuery();
cmd.CommandText = "SELECT [Field1], [Field2] FROM [TableName]";
DataTable dt = new DataTable();
dt.Load(cmd.ExecuteReader());
Regex objectRegex = new Regex(#"^OBJECT:\(\[(?<database>[^\]]+)\]\.\[(?<schema>[^\]]+)\]\.\[(?<table>[^\]]+)\]\.\[(?<field>[^\]]+)\]\)$", RegexOptions.ExplicitCapture);
List<string> lstTables = new List<string>();
foreach (DataRow row in dt.Rows)
{
string argument = row["Argument"].ToString();
if (!String.IsNullOrEmpty(argument))
{
Match m = objectRegex.Match(argument);
if (m.Success)
{
string table = m.Groups["schema"] + "." + m.Groups["table"];
if (!lstTables.Contains(table))
{
lstTables.Add(table);
}
}
}
}
Console.WriteLine("Query uses the following tables: " + String.Join(", ", lstTables));
This will deal with all forms of query name and return all tables which are involved in the query, no matter how they are included.
If this is the same pattern all of the time then:
string tableName = strCommand.Split(' ', strCommand)[4];
but if you can add / remove fields just iterate through the splitted string and search for "From", and the next string will be your table name
I would say- what is after "From" as a more reliable way of getting the table name. Loop through the array created, when you reach "From", the next one is the table.
This is the Method which gives us tablename just change the SQL query string, connection String
Works with simple query, joins too
public static List<string> getTablenames(string connString, string QueryString)
{
SqlConnection con = new SqlConnection(connString);
con.Open();
DataTable dt = con.GetSchema("Tables");
List<string> getTableName = new List<string>();
List<string> tablenames = new List<string>();
foreach (DataRow dr in dt.Rows)
tablenames.Add(dr[2].ToString());
for (int i = 0; i < dt.Rows.Count; i++)
{
string myTable = tablenames[i];
Boolean checkMyTable = QueryString.Contains(myTable);
if (checkMyTable == true)
getTableName.Add(myTable);
}
con.Close();
return getTableName;
}
You can use the substring (This way it does not matter how many column you have to select)
string table = strCommand.ToLower().Substring(strCommand.IndexOf("FROM".ToLower())).Split(' ')[0];
ISun's answer met my needs but one change is required to get the table name:
string table = strCommand.ToLower().Substring(strCommand.IndexOf("FROM".ToLower())).Split(' ')[1];
not
string table = strCommand.ToLower().Substring(strCommand.IndexOf("FROM".ToLower())).Split(' ')[0];
If you want a solution in SQL, try this
declare #q varchar(1000) = 'Select [Feild1], [Feild2] From TableName Order By [Feild1] desc',
#tableName varchar(100) = '',
#temp varchar(1000),
#temp2 char(1)
declare #frmIndex int = CHARINDEX('From', #q, 0);
declare #flag int = 0, #counter int = 1;
select #temp = SUBSTRING(#q, #frmIndex, len(#q))
set #temp = LTRIM(REPLACE(#temp,'From',''))
while(#flag <> 1)
begin
set #temp2 = SUBSTRING(#temp, #counter, 1)
if(#temp2 = ' ')
set #flag = 1
select #tableName = #tableName + #temp2
set #counter = #counter + 1
end
select #tableName as TableName

Categories