I have this DataSet which calls my Stored Procedure and returns a list of integers. How can I extract the list of integers which I could store in a variable be it a collection which grows in size such as a List<T> or a primitive data type like an array of integers.
Below is my code:
private DataSet getSubGroupsBelongingToUser()
{
DataTable variable;
DataSet DS;
myConnectionString = ConfigurationManager.ConnectionStrings["FSK_ServiceMonitor_Users_Management.Properties.Settings.FSK_ServiceMonitorConnectionString"].ConnectionString;
using (mySQLConnection = new SqlConnection(myConnectionString))
{
SqlParameter param = new SqlParameter("#UserId", getUserID(cbxSelectUser.Text));
DS = GetData("Test", param);
variable = DS.Tables[0];
}
return DS;
}
When I hover over the DS magnifier (refer to image):
I want to retrieve and store that list of integers somewhere. How can I go about doing this? All the examples I came across online make use of linq and that is not applicable here since I am getting the results from my stored procedure which requires one input parameter. Here is the definition of the stored procedure below:
create proc [dbo].[Test]
#UserId smallint
as
begin
select DepartmentSubGroupId from DepartmentSubGroupUser
where UserId= #UserId
end
GO
So essentially when you pass in a UserId, you should get those values. I am using SQL Server as my DBMS.
The simplest and most efficient approach would be to not use a DataSet/DataTable at all:
private List<int> GetSubGroupsBelongingToUser()
{
List<int> list = new List<int>();
using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["FSK_ServiceMonitor_Users_Management.Properties.Settings.FSK_ServiceMonitorConnectionString"].ConnectionString))
using (var cmd = new SqlCommand("Test", con))
{
cmd.CommandType = CommandType.StoredProcedure;
var param = new SqlParameter("#UserId", SqlDbType.Int).Value = int.Parse(cbxSelectUser.Text);
cmd.Parameters.Add(param);
con.Open();
using (var rd = cmd.ExecuteReader())
{
while (rd.Read()) list.Add(rd.GetInt32(0));
}
} // no need to close the connection with the using
return list;
}
If you insist on the DataTable, at least it's more conscise:
return DS.Tables[0].AsEnumerable().Select(r => r.Field<int>(0)).ToList();
As #David pointed out the simplest option would be to use a SqlDataReader and loop through all the records.
However, if your heart is set on DataTables then all you need to do is to iterate all rows from the result table, grab the value from column DepartmentSubGroupId and add it to a list. You can do that with Linq also like this:
return DS.Tables[0].Rows
.Cast<DataRow>() // Rows is an ICollection and you need to cast each item
.Select(r => (int)r["DepartmentSubGroupId"]) // For each row get the value from column DepartmentSubGroupId
.ToList();
Before I saw #Tim's solution, I had already prepared this (which works just as well as most of the above solutions):
public List<int> getSubGroupsBelongingToUser()
{
List<int> DepartmentSubGroupIds = new List<int>();
myConnectionString = ConfigurationManager.ConnectionStrings["FSK_ServiceMonitor_Users_Management.Properties.Settings.FSK_ServiceMonitorConnectionString"].ConnectionString;
using (mySQLConnection = new SqlConnection(myConnectionString))
{
SqlParameter parameter = new SqlParameter("#UserId", getUserID(cbxSelectUser.Text));
mySQLCommand = new SqlCommand("Test", mySQLConnection);
mySQLCommand.CommandType = CommandType.StoredProcedure;
mySQLCommand.Parameters.Add(parameter);
mySQLConnection.ConnectionString = myConnectionString;
mySQLConnection.Open();
SqlDataReader sqlDataReader = mySQLCommand.ExecuteReader();
while (sqlDataReader.Read())
{
DepartmentSubGroupIds.Add(Convert.ToInt32(sqlDataReader["DepartmentSubGroupId"]));
}
}
return DepartmentSubGroupIds;
}
Thanks everybody, much appreciated.
Related
I know how to pass one parameter to an sql query but i want to create a function to pass multiple params that will have differents type and here im stuck.
public List<T> RawSql<T>(string query, params object[] parameters)
{
var command = context.Database.GetDbConnection().CreateCommand();
command.CommandText = query;
command.CommandType = CommandType.Text;
SqlParameter parameter = new SqlParameter();
parameter.ParameterName = "#bookId";
parameter.SqlDbType = SqlDbType.Int;
parameter.Value = parameters[0];
command.Parameters.Add(parameter);
var result = command.ExecuteReader())
return result;
}
Usage :
var rows = helper.RawSql("myStoreProc #bookId", x=> new Book { Id = (bool)x[0] }, bookId);
But how i can change the RawSql function to pass multiple parameters like this :
var rows = helper.RawSql("myStoreProc #bookId, #authorName", x=> new Book { Id = (bool)x[0] }, bookId, authorName);
I would also suggest using Dapper instead of reinventing the wheel - but if you can't for some reason, I would change the method signature to accept params SqlParameter[] parameters instead of params object[] parameters - and then all you need to do in the method is command.Parameters.AddRange(parameters);.
As Marc Gravel wrote in his comment - naming the parameters is going to be the biggest problem if you are simply using object[].
Here is a method I wrote to compare values from two different days:
public DataTable sqlToDTCompare(string conStr, string stpName, DateTime startDate, DateTime endDate, int percent)
{
//receives connection string and stored procedure name
//then returns populated data table
DataTable dt = new DataTable();
using (var con = new SqlConnection(conStr))
using (var cmd = new SqlCommand(stpName, con))
using (var da = new SqlDataAdapter(cmd))
{
cmd.Parameters.Add("#StartDate", SqlDbType.Date).Value = startDate;
cmd.Parameters.Add("#EndDate", SqlDbType.Date).Value = endDate;
cmd.Parameters.Add("#Percent", SqlDbType.Int).Value = percent;
cmd.CommandType = CommandType.StoredProcedure;
da.Fill(dt);
}
return dt;
}
This method then returns that data to a DataTable (was what I needed at time of writing). You would be able to use this , with modifying to be of better fit for your needs.
What you're looking to use is something along:
SqlCommand.Parameters.Add("#Param1", SqlDbType.Type).Value = param1;
SqlCommand.Parameters.Add("#Param2", SqlDbType.Type).Value = param2;
SqlCommand.Parameters.Add("#Param3", SqlDbType.Type).Value = param3;
.....
Where .Type in SqlDbType.Type can be changed to matche whatever SQL datatype you're needing (ex. SqlDbType.Date).
I have previously done implementations along these lines.
public IEnumerable<SampleModel> RetrieveSampleByFilter(string query, params SqlParameter[] parameters)
{
using(var connection = new SqlConnection(dbConnection))
using(var command = new SqlCommand(query, connection))
{
connection.Open();
if(parameters.Length > 0)
foreach(var parameter in parameters)
command.Parameters.Add(parameter);
// Could also do, instead of loop:
// command.Parameters.AddRange(parameters);
using(var reader = command.ExecuteReader())
while(reader != null)
yield return new Sample()
{
Id = reader["Id"],
...
}
}
}
I actually wrote an extension method to read the values returned back into my object, but this allows you to pass a query and a series of parameters to simply return your object.
I would look into Dapper, saves a lot of time. But I find the problem with trying to reuse with the above type of solution creates a bit of tightly coupling often.
By doing this approach you push specific information about your query elsewhere, which separates logic directly out of the repository and tightly couples to another dependency and knowledge.
I have a SQL Server stored procedure that I run two select statements. I can easily return one select statement and store it in a DataTable but how do I use two?
How can I set my variable countfromfirstselectstatement equal to the count returned from my first select statement and how can I set my variable countfromsecondselectstatement equal to the count returned from the second select.
private void ReturnTwoSelectStatements()
{
DataSet dt112 = new DataSet();
using (var con = new SqlConnection(connectionString))
{
using (var cmd = new SqlCommand("Return2SelectStatements", con))
{
using (var da = new SqlDataAdapter(cmd))
{
cmd.CommandType = CommandType.StoredProcedure;
da.Fill(dt112);
}
}
}
DataTable table1 = dt112.Tables[0];
DataTable table2 = dt112.Tables[1];
string countfromFirstSelectStatement = table1.Rows.Count().ToString();
string countfromSecondSelectStatement = table2.Rows.Count().ToString();
//I only want to iterate the data from the 1st select statement
foreach (DataRow row in dt112.Rows)
{
}
}
You could also directly use the DbDataReader (cmd.ExecuteReader()), which gives you NextResult to advance to the next result set. DataTable.Load allows you to load rows from the data reader to the table.
DbDataAdapter is really a bit of an overkill for just reading data into a data table. It's designed to allow the whole CRUD-breadth of operation for controls that are abstracted away from the real source of data, which isn't really your case.
Sample:
using (var reader = cmd.ExecuteReader())
{
dataTable1.Load(reader);
if (!reader.NextResult()) throw SomethingWhenTheresNoSecondResultSet();
dataTable2.Load(reader);
}
Fill a Dataset with both select statements and access it:
....
DataSet dataSet = new DataSet();
da.Fill(dataSet);
....
DataTable table1 = dataSet.Tables[0];
DataTable table2 = dataSet.Tables[1];
string countfromFirstSelectStatement = table1.Rows.Count.ToString();
string countfromSecondSelectStatement = table2.Rows.Count.ToString();
I have a stored procedure that selects and returns a list of years. In sql server I call it like this:
DECLARE #return_value int
EXEC #return_value = [dbo].[TestName]
#del= 0
SELECT 'Return Value' = #return_value
In order to receive the list.
My SP looks like this:
USE [TestTable]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[TestName] (#delvarchar(7))
AS
BEGIN
SELECT YEAR( [added]) AS year FROM [MyTable]
GROUP BY YEAR( [added])
ORDER BY YEAR( [added]) DESC
END
I want to do the same from c# though and pass the values in a List.
What I am trying is:
using (var conn = new SqlConnection(constr))
using (var command = new SqlCommand("TestName", conn)
{
CommandType = CommandType.StoredProcedure
})
{
command.Parameters.AddWithValue("#del", del);
SqlParameter retval = command.Parameters.Add("#return_value", SqlDbType.VarChar);
retval.Direction = ParameterDirection.ReturnValue;
conn.Open();
command.ExecuteNonQuery();
int retunvalue = (int)command.Parameters["#return_value"].Value;
conn.Close();
return retunvalue;
}
This does not return any values though, instead it only returns 0. What am I doing wrong and how can I get the list inside a variable as specified?
Editing the code following John Hanna's advise I have something like this:
public List<string> getYears(string constr, int del)
{
using (var conn = new SqlConnection(constr))
using (var command = new SqlCommand("TestName", conn)
{
CommandType = CommandType.StoredProcedure
})
{
command.Parameters.AddWithValue("#del", del);
List<string> retunvalue = new List<string>();
conn.Open();
SqlDataReader reader;
reader = command.ExecuteReader();
conn.Close();
return retunvalue;
}
}
And by adding a breakpoint in order to explore reader I see that it is only contains errors:
Depth = '(reader).Depth' threw an exception of type 'System.InvalidOperationException'
As for Krishna's answer, dtList was empty with a count of 0, and I am not sure how to implement Badhon's suggestion.
ExecuteNonQuery() is so called because its for use with something that doesn't query the data. The 0 you get back is the number of rows the command changed.
Instead use ExecuteReader() and you will get back a SqlDataReader object that you can call Read() on to move through rows and then examine the details of each.
If you want to return that to another method use ExecuteReader(CommandBehavior.CloseConnection) and then rather than Close() or Dispose() the connection after you are finished, Close() or Dispose() the reader and that will close the connection then.
If you only have one row with one column (or for some reason only care about the first column of the first row even though there is more), then ExecuteScalar() is a convenient way to get just that single value.
You shouldn't use ExecuteNonQuery, rather use ExecuteDataSet as follow:
public List<DSM_DocPropIdentify> GetDocPropIdentify(string docPropIdentifyID, string action, out string errorNumber)
{
errorNumber = string.Empty;
List<DSM_DocPropIdentify> docPropIdentifyList = new List<DSM_DocPropIdentify>();
DatabaseProviderFactory factory = new DatabaseProviderFactory();
SqlDatabase db = factory.CreateDefault() as SqlDatabase;
using (DbCommand dbCommandWrapper = db.GetStoredProcCommand("GetDocPropIdentify"))
{
// Set parameters
db.AddInParameter(dbCommandWrapper, "#DocPropIdentifyID", SqlDbType.VarChar, docPropIdentifyID);
db.AddOutParameter(dbCommandWrapper, spStatusParam, DbType.String, 10);
// Execute SP.
DataSet ds = db.ExecuteDataSet(dbCommandWrapper);
if (!db.GetParameterValue(dbCommandWrapper, spStatusParam).IsNullOrZero())
{
// Get the error number, if error occurred.
errorNumber = db.GetParameterValue(dbCommandWrapper, spStatusParam).PrefixErrorCode();
}
else
{
if (ds.Tables[0].Rows.Count > 0)
{
DataTable dt1 = ds.Tables[0];
docPropIdentifyList = dt1.AsEnumerable().Select(reader => new DSM_DocPropIdentify
{
DocPropIdentifyID = reader.GetString("DocPropIdentifyID"),
DocPropertyID = reader.GetString("DocPropertyID"),
DocCategoryID = reader.GetString("DocCategoryID"),
DocTypeID = reader.GetString("DocTypeID"),
OwnerID = reader.GetString("OwnerID"),
IdentificationCode = reader.GetString("IdentificationCode"),
IdentificationSL = reader.GetString("IdentificationSL"),
AttributeGroup = reader.GetString("AttributeGroup"),
IdentificationAttribute = reader.GetString("IdentificationAttribute"),
IsRequired = reader.GetInt16("IsRequired"),
IsAuto = reader.GetInt16("IsAuto"),
SetOn = reader.GetString("SetOn"),
SetBy = reader.GetString("SetBy"),
ModifiedOn = reader.GetString("ModifiedOn"),
ModifiedBy = reader.GetString("ModifiedBy"),
Status = reader.GetInt32("Status"),
Remarks = reader.GetString("Remarks")
}).ToList();
}
}
}
return docPropIdentifyList;
}
Is dbo.TestName returns you a table or a value. If that returns you a table, then you would have to execute DataAdapter or execute DataReader. You should replace the above statement
as
DataTable dtList=new DataTable();
SqlDataAdapter adapter=new SqlDataAdapter();
adapter.SelectCommand=command;
adapter.Fill(dtList);
Then you can iterate through the datatable and add that to your list
List<Object> listObj=new List<Object>();
foreach(var rows in dtList.Rows)
{
listObj.Add(rows["Column_name"]);
}
I analysed your query, and found that, the statement
DECLARE #return_value int
EXEC #return_value = [dbo].[TestName]
#del= 0
SELECT 'Return Value' = #return_value
returns multiple tables. You can remove the last statement which is redundant.
SELECT 'Return Value' = #return_value
The datatable will now be populated with values.
Let me know if that works.
I am trying to get the data from a MySQL table and storing it into a dictionary. I know I can do that with a loop but DB table contains more than a million tuples and will slow the progress. Is there any way to do that without having to loop all the entries?
DB table has two columns, 1st(key) float type and 2nd(value) varchar.
My Current approach:
public static Dictionary<Single, string> GetData()
{
Dictionary<Single, string> dic= new Dictionary<Single, string>();
string query = "select * from table;";
if (OpenConnection() == true)
{
try
{
MySqlCommand cmd = new MySqlCommand(query, connection);
MySqlDataReader reader = cmd.ExecuteReader();
if (reader.Read())
{
//Something to store DB columns into dictionary without having to loop.
}
reader.Close();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
return dic;
}
Next is what you want:
try
{
MySqlCommand cmd = new MySqlCommand(query, connection);
MySqlDataAdapter msda = new MySqlDataAdapter(cmd);
DataSet ds = new DataSet();
msda.Fill(ds);
your_control.DataSource = ds;
your_control.DataBind();
//your control means some repeater, or gridview, or anything else, which may show all your elements together.
}
But you also should consider that if you have millions of records, it would be better to make them selected partially. For example, 100 records per page, using row_number or similar methods
I have a problem with how to fill an array list from a data reader
string queryDTL = " SELECT * FROM tbl1 ";
connection.Connect();
cmd = new OracleCommand(queryDTL, connection.getConnection());
dr_DTL = qcmd2.ExecuteReader();
ArrayList RecordsInfo = new ArrayList();
while (dr_DTL.Read())
{
RecordsInfo = dr_DTL["number"].ToString();
}
The problem is the datareader contain alot of info other than the number but I don't know how to put them in their correct position.
I am still a beginner in this sorry if it sounds stupid.
You can't put a String in an ArrayList. You have to add the string to the list.
Ex. :
ArrayList RecordsInfo = new ArrayList();
while (dr_DTL.Read())
{
RecordsInfo.Add(dr_DTL["number"].ToString());
}
If you want a list of String the best way is using List<String>.
Ex. :
List<String> RecordsInfo = new List<String>();
while (dr_DTL.Read())
{
RecordsInfo.Add(dr_DTL["number"].ToString());
}
I think you would be better off with a DataTable and an adapter:
SqlConnection Conn = new SqlConnection(MyConnectionString);
Conn.Open();
SqlDataAdapter Adapter = new SqlDataAdapter("Select * from Employees", Conn);
DataTable Employees = new DataTable();
Adapter.Fill(Employees);
GridView1.DataSource = Employees;
GridView1.DataBind();
Of course in your case, use the Oracle versions of the objects.
Hi you have to do RecordInfo.Add, currently you are reassigning the whole arraylist...
Also if you are retrieving too many columns, just query the columns you need, in your case number, not simply SELECT * ...