System.IndexOutOfRangeException when trying to save results from a stored proecedure - sql

I have a SQL Server stored procedures with this output:
unit_id on_date notes type_code type_order status (No column name) (No column name)
3 2016-12-08 00:00:00.000 AVL -1 D NULL 16
3 2016-12-08 00:00:00.000 RSU 1 D 3 2
3 2016-12-08 00:00:00.000 TOW 2 D 6 5
.......etc
What I am trying to do it get these rows a columns to I can display them in a grid (spreadsheet like) view, and use them as variables in a bar graph.
I've tried the code (in my controller)
var model = new List<ResultsModel>();
SqlCommand command3 = new SqlCommand("dbo.pr_name");
command3.Parameters.Add(new SqlParameter { ParameterName = "#from", SqlDbType = System.Data.SqlDbType.DateTime, Value = NowDate });
command3.Parameters.Add(new SqlParameter { ParameterName = "#to", SqlDbType = System.Data.SqlDbType.DateTime, Value = "2017-09-21 00:00:00" });
command3.Parameters.Add(new SqlParameter { ParameterName = "#method", SqlDbType = System.Data.SqlDbType.Int, Value = 3 });
command3.CommandType = System.Data.CommandType.StoredProcedure;
using (var SPOutput3 = command.ExecuteReader())
{
model.Add(new ResultsModel()
{
unit_id = (Int32)SPOutput3["unit_id"],
on_date = (DateTimeOffset)SPOutput3["on_date"],
notes = SPOutput3["notes"].ToString(),
type_code = (string)SPOutput3["type_code"]
// other properties
});
return View(model);
}
and in my view
#*#foreach (var item in Model)
{
<tr>
<td>#item.on_date</td>
<td>#item.type_code</td>
</tr>
}*#
The code breaks at the line:
unit_id = (Int32)SPOutput3["unit_id"],
with an error System.IndexOutOfRangeException.
If I comment out that line, the error moves onto the next one etc.
The advise was after is: is the error telling me that there is no columns called unit_id in the output received? even thought the output from the SSMS shows it?
.. and what I can do to fix this?
Also....if the column has no name, how can I assign it ..like unit_id, on_date etc ?
Thanks

Well, first off, you need to call Read in a while loop:
using (var SPOutput3 = command.ExecuteReader())
{
while (SPOutput3.Read())
{
...
}
}
Then, inside the while loop, you're dealing with an individual row. So you can do:
while (SPOutput3.Read())
{
var unit_id = SPOutput3["unit_id"] as int?;
}
You want to use as rather than a direct cast here so you can stave off potential issues if bad data is returned or the type isn't what you think it is. If you need a non-nullable value, then you can simply use the null coalesce operator to provide a default:
SPOutput3["unit_id"] as int? ?? 0;

Related

ASP.Net Core SelectList not displaying selected item

Using ASP.Net core 2.2 in VS 2017 (MVC):
I have a wizard based screen that has a drop down list of Organizations.
When I load a record from the Database, I want to set the select list to the values in the DB.
Spoiler alert: The specified Item is not selected when the page is initially displayed.
When the item is manually selected, and the ‘Next’ button is clicked and then the Prev button is clicked, returning us to the first page, the item is selected just as I intend.
None of the methods below set the ‘Selected’ property of the selected item (“0403”) when loading the Model in the controller for the first display.
The compare in Method 2 never evaluates to true even though I can see the compared values are equal while debugging.
Method 3 does not find the item even though I can see the compared values are equal while debugging
Method 4 does not find the item even though I can see the compared values are equal while debugging
This is all happening in the Controller, so please do not suggest changing the name of the dropdown in the View.
**Org table
ORG ID OrgName**
0004 Org 4
0007 Org 7
0008 Org 8
0403 Org 403
This is my source query:
var orgsQuery = from s in _context.Orgs
orderby s.OrgID
select s;
These are the various ways I have tried building the select List in the Controller:
1).
SelectList oList = new SelectList(orgsQuery.AsNoTracking(), "OrgID", "OrgName", selectedOrg);
2).
List<SelectListItem> oList = new List<SelectListItem>();
foreach ( var item in OrgsQuery)
{
oList.Add(new SelectListItem()
{
Text = item.OrgName,
Value = item.OrgID,
Selected = (item.OrgID == (string)selectedOrg ? true : false)
});
}
3).
if (selectedOrg != null)
{
oList = new SelectList(OrgsQuery.AsNoTracking(),"OrgID", "OrgName", OrgsQuery.First(s =>
s.OrgID == (string)selectedOrg));
}
else
{
oList = new SelectList(OrgsQuery.AsNoTracking(), "OrgID", "OrgName", selectedOrg);
}
4).
SelectList oList =
new SelectList(_context.Org.OrderBy(r => r.OrgID),
_context.Org.SingleOrDefault(s => s.OrgID == (string)selectedOrg))
Well, assume that selectedOrg type is numeric, than it should be convert to formatted string by following the OrgID format:
var formattedSelectedOrg = String.Format("{0:0000}", selectedOrg);
Or
// if selectedOrg is nullable numeric type
var formattedSelectedOrg = selectedOrg?.ToString("0000");
// if selectedOrg is base numeric type
var formattedSelectedOrg = selectedOrg.ToString("0000");
// if selectedOrg is String type (nullable)
var formattedSelectedOrg = selectedOrg?.Trim().PadLeft(4, '0');
Or
var number;
var formattedSelectedOrg =
String.Format("{0:0000}", Int32.TryParse(selectedOrg?.Trim(), out number) ? number : 0);
So it would be comparable when applied to your codes:
// assume selectedOrg is base numeric type
Selected = item.OrgID == selectedOrg.ToString("0000")
Or
// if selectedOrg is String type (nullable)
Selected = item.OrgID == selectedOrg?.Trim().PadLeft(4, '0')
Or
// if you prefer to use predefined formatted string
Selected = item.OrgID == formattedSelectedOrg
And
// assume selectedOrg is base numeric type
s => s.OrgID == selectedOrg.ToString("0000")
Or
// if selectedOrg is String type (nullable)
s => s.OrgID == selectedOrg?.Trim().PadLeft(4, '0')
Or
// if you prefer to use predefined formatted string
s => s.OrgID == formattedSelectedOrg
Check this out:
Custom numeric format strings
String.PadLeft Method
Also this important briefcase:
Pad left with zeroes
Hope this could helps. Enjoy your coding!

SQl query to Break up rows to batches

I have a table like this
OrderID Product Quantity
1 A 800
2 B 700
3 C 300
I need a select query in order to split the total quantity of 1800 to a number of batches (eg 3) like this
BatchNo Product Quantity
1 A 600
2 A 200
2 B 400
3 B 300
3 C 300
Is there any way to do this as a select query ?
I use SQL 2016
ceiling(cast(row_number()(order by (select 1)) as decimal (10, 2)) / 3) as BatchNo
This should do what you require. Just add it to your select statement. You can change the number you are dividing by splitting it up into more batches.
Imho the fastest way to do this is write for example CLR SqlProcedure that would read data row by row and produce output record every time "batch" has 300 or more using SqlContext.Pipe.SendResultsStart() / SqlContext.Pipe.SendResultsEnd() / SqlContext.Pipe.SendResultsRow() . It is easy to produce output data in single pass with c#, not so much in tsql
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void MakeBatches()
{
using (var connection = new SqlConnection("context connection=true"))
{
connection.Open();
var command = new SqlCommand(string.Format("SELECT [...]"), connection);
var reader = command.ExecuteReader();
using (reader)
{
var outputRecord = new SqlDataRecord(new SqlMetaData[]
{
new SqlMetaData("BatchNo", SqlDbType.Int),
new SqlMetaData("Product", SqlDbType.Char),
new SqlMetaData("Quantity", SqlDbType.Int)
}
);
SqlContext.Pipe.SendResultsStart(outputRecord);
while (reader.Read())
{
var orderId = reader.GetInt32(0);
var product = (char)reader.GetValue(1);
var quantity = reader.GetInt32(2);
if ([...]) //put some logic here
{
outputRecord.SetInt32(0, batchNo_value_here);
[...] //insert record here
SqlContext.Pipe.SendResultsRow(outputRecord);
}
}
SqlContext.Pipe.SendResultsEnd();
}
}
}
}

Outputting values not equal to certain values in yii2

I would like to output variables not equal to certain values but it returns an error of
Failed to prepare SQL: SELECT * FROM `tblsuunit` WHERE `unitid` != :qp0
There are two models the first model where am getting the array of ids
public function actionSunits($id){
$unitslocation = new Unitslocation();
$id2 = Unitslocation::find()->where(['officelocationid'=>$id])->all();
foreach( $id2 as $ids){
print_r($ids['unitid']."<br>");
}
}
This outputs the ids as
8
9
11
12
13
14
16
I would then like to take the id and compare another model(units model) and get the id values not similar to the above and output then
So i have added
$idall = Units::find()->where(['!=', 'unitid', $ids])->all();
So the whole controller action becomes
public function actionSunits($id){
$unitslocation = new Unitslocation();
$id2 = Unitslocation::find()->where(['officelocationid'=>$id])->all();
foreach( $id2 as $ids){
$idall = Units::find()->where(['!=', 'unitid', $ids])->all();
}
var_dump($idall);
}
This is the units model table:
If it were working it should return 7 and 10
What could be wrong..
You should fix your code and simply use a not in condition, e.g. :
// $uls will be an array of Unitslocation objects
$uls = Unitslocation::find()->where(['officelocationid'=>$id])->all();
// $uids will contain the unitids
$uids = \yii\helpers\ArrayHelper::getColumn($uls, 'unitid');
// then simply use a not in condition
$units = Units::find()->where(['not in', 'unitid', $uids])->all();
$idall = \yii\helpers\ArrayHelper::getColumn($units, 'unitid');
Read more about ActiveQuery::where() and ArrayHelper::getColumn().
Try with:
$idall = Units::find()->where(['not in','unitid',$ids])->all();
Info: https://github.com/yiisoft/yii2/blob/master/docs/guide/db-query-builder.md
operand 1 should be a column or DB expression. Operand 2 can be either
an array or a Query object.

using sql reader to check for nulls when setting multiple values

I have a sql command that is picking up a row in my DB but sometimes one of the datetime values may be null.
example:
var reader = command.ExecuteReader();
if (reader.HasRows)
{
List<AdmissionsVm> appDetailsOut = new List<AdmissionsVm>();
while (reader.Read())
{
appListOut.Add(new AdmissionsVm
{
Parish = Convert.ToString(reader.GetValue(40)),
CofE = Convert.ToBoolean(reader.GetValue(41)),
OtherFaith = Convert.ToString(reader.GetValue(42)),
PrefSiblingName1 = Convert.ToString(reader.GetValue(43)),
if (!reader.GetValue(44).IsDbNull){SiblingDateOfBirth = Convert.ToDateTime(reader.GetValue(44))}
SiblingGender = Convert.ToString(reader.GetValue(45))
});
}
}
I am actually bringing back a lot of details but when the siblingdateofbirth is null, i cant seem to check it as i am getting errors with fields that have been added afterwards
any help would be appreciated
Its often better to specify the column name instead of the column position because if the query for some reason changes the order in which its returning columns, you may need to change the params of all the GetValue calls.
To check for null try something like this
if (!reader.IsDBNull(reader.GetOrdinal("YourColumnNameForPosition44")))
{SiblingDateOfBirth = Convert.ToDateTime(reader.GetString(reader.GetOrdinal("YourColumnAgain"))}

NHibernate query count

I am new to NHibernate and I want to have a count of rows from database. Below is my code,
SearchTemplate template = new SearchTemplate();
template.Criteria = DetachedCriteria.For(typeof(hotel));
template.Criteria.Add(Restrictions.Lt("CheckOutDate", SelDate) || Restrictions.Eq("CheckOutDate", SelDate));
template.Criteria.Add(Restrictions.Eq("Canceled", "False"));
int count = template.Criteria.SetProjection(Projections.Count("ID"));
It gives me an error when I try to compile app that says
"Cannot implicitly convert type 'NHibernate.Criterion.DetachedCriteria' to 'int'"
I want to have a count of rows of the table hotel..
You want to use GetExecutableCriteria:
SearchTemplate template = new SearchTemplate();
template.Criteria = DetachedCriteria.For(typeof(hotel));
template.Criteria.Add(Restrictions.Lt("CheckOutDate", SelDate) || Restrictions.Eq("CheckOutDate", SelDate));
template.Criteria.Add(Restrictions.Eq("Canceled", "False"));
var count = DoCount(template.Criteria, session /* your session */);
public long DoCount(DetachedCriteria criteria, ISession session)
{
return Convert.ToInt64(criteria.GetExecutableCriteria(session)
.SetProjection(Projections.RowCountInt64())
.UniqueResult());
}
On a side note, you should take a look at using NHibernate.Linq:
var result = (from h in Session.Linq<Hotel>()
where h.CheckOutDate <= SelDate
where h.Canceled != true
select h).Count();
More information here.