C# SQLite3 Select if null - c#

I want the code to do something if the thing its looking for doesn't exist in the DB. This is what I am working with currently:
const string cs = #"Data Source= ..\..\..\database.sqlite3";
await using var con = new SQLiteConnection(cs);
con.Open();
await using var cmd = new SQLiteCommand(con);
member ??= ctx.Member;
cmd.CommandText = #"SELECT iq FROM users WHERE id = $user_id";
cmd.Parameters.AddWithValue("$user_id", member.Id);
var r = cmd.ExecuteReader();
while (r.Read())
{
if (r["iq"] == null)
{
var random = new Random();
var iq = random.Next(1, 200);
await cmd.DisposeAsync();
cmd.CommandText = "INSERT INTO users(id, iq) VALUES($member_id, $id)";
cmd.Parameters.AddWithValue("$iq", iq);
cmd.Parameters.AddWithValue("$member_id", member.Id);
cmd.ExecuteNonQuery();
var embed = new DiscordEmbedBuilder
{
Title = $"IQ of {member.Username} is {iq} :brain:",
Color = DiscordColor.Cyan
};
await ctx.Channel.SendMessageAsync(embed);
}
else
{
var iqFinal = r["iq"];
var embed = new DiscordEmbedBuilder
{
Title = $"IQ of {member.Username} is {iqFinal} :brain:",
Color = DiscordColor.Cyan
};
await ctx.Channel.SendMessageAsync(embed);
}
}
I really don't know how do make that happen, I've tried try catch blocks but they didn't seem to work either. How can I do this?

I assume that id is the primary key of the table, so you don't expect more than 1 rows from the statement:
SELECT iq FROM users WHERE id = $user_id
The problem with this statement is that it may not return any rows if $user_id does not exist in the table, so in this case r.Read() will return false and the code inside the while loop will never be executed.
One way to do what you want is to use aggregation:
SELECT MAX(iq) AS iq FROM users WHERE id = $user_id
which always returns a row: either the row with the iq where id = $user_id or null if $user_id does not exist in the table.
Also you should change the while loop to just:
if (r.Read()) {
.....................
}
or better just:
r.Read();
because the aggregation query is guaranteed that will return exactly 1 row.

Related

C# Winform - OracleDataAdapter not persisting the records immediately

I am making changes in an old C# WinForm application.
In the code below, when the DoWork() function is invoked. It calls the two functions PerformOperation1 and PerformOperation2. Both functions have a very similar body. The only difference is that they both update different fields of the same database table.
Finally, when they both have performed their job, we fetch the records using an OracleDataAdapter.
The count of the rows returned is 0 in the code. However, if I execute the query on the database straight away, it returns some rows. This means somehow the records updated by the PerformOperation2 are not pushed to the database at the time when we call the Fill on the DataAapter.
public void DoWork()
{
PerformOperation1();
PerformOperation2();
var sql = "select * from results where result_id = 1 and is_valid = 'Y'";
var table = new DataTable();
var data = new OracleDataAdapter(new OracleCommand(sql, base.Connection));
data.Fill(table);
var count = data.Rows.Count; //It returns 0 But when execute the same query on database, it returns rows.
}
public void PerformOperation1()
{
string sql = "select seq_1, product, count_1, count_2 from results where result_id = 1";
string updateSQL = "update results set count_1 = :count_1, count_2 = :count_2 WHERE seq_1 = :seq_1";
var selectCmd = new OracleCommand(sql, base.Connection);
selectCmd.CommandType = CommandType.Text;
var adapter = new OracleDataAdapter(lCmd);
adapter.UpdateCommand = new OracleCommand(updateSQL, base.Connection);
adapter.UpdateCommand.CommandType = CommandType.Text;
AddCommandParameter(adapter.UpdateCommand, ":count_1", DbType.Double, 11, "count_1");
AddCommandParameter(adapter.UpdateCommand, ":count_2", DbType.Double, 11, "count_2");
var data = new DataSet();
adapter.Fill(data);
foreach (var row in data.Tables[0].Rows)
{
row["count_1"] = GetCount1(row["seq1"]); //Returns Count1
row["count_2"] = GetCount2(row["seq1"]); //Returns Count2
//Forces an immediate garbage collection of all generations
GC.Collect();
GC.WaitForPendingFinalizers();
}
adapter.Update(data);
data.AcceptChanges();
}
public void PerformOperation2()
{
string sql = "select seq_1, product, is_valid from results where result_id = 1";
string updateSQL = "update results set is_valid = :is_valid WHERE seq_1 = :seq_1";
//Does exactly the samething like PerformOperation1 function above.
//100% same code.
//Only difference is that it updates different column named is_valid (with value 'Y' or 'N')
}
public void AddCommandParameter(DbCommand aoCommand, string asParamName, DbType aoDataType, int aiSize, string asColName)
{
if (aoCommand is OracleCommand)
{
OracleCommand loCommand = (OracleCommand)aoCommand;
OracleType loOraDataType = ConvertToOracleType(aoDataType);
loCommand.Parameters.Add(asParamName, loOraDataType, aiSize, asColName);
}
}
Any idea why this is happening, please?
I figured it out. It was not a coding issue. It was some underlying database view problem.

Read all record in sql table using SqlDataReader

I want to read all records from "product" table and create objects from each records.
it only gets one records from the database, any ideas might help ?
public IReadOnlyList<Product> Search(string name)
{
var result = new List<Product>();
using (var conn = new SqlConnection(connectionString))
{
if (name == null)
{
var command = new SqlCommand("SELECT * FROM Product ", conn);
conn.Open();
using var reader = command.ExecuteReader();
{
while (reader.Read())
{
var prod = new Product((int)reader["ID"], (string)reader["Name"],
(double)reader["Price"], (int)reader["Stock"], (int)reader["VATID"],
(string)reader["Description"]);
result.Add(prod);
reader.NextResult();
}
reader.Close();
conn.Close();
return result;
};
}
}
If you have several result sets, you should loop over them, i.e. you should put one more outer loop, e.g.
using var reader = command.ExecuteReader();
do {
while (reader.Read()) {
var prod = new Product(
Convert.ToInt32(reader["ID"]),
Convert.ToString(reader["Name"]),
Convert.ToDouble(reader["Price"]), // decimal will be better for money
Convert.ToInt32(reader["Stock"]),
Convert.ToInt32(reader["VATID"]),
Convert.ToString(reader["Description"])
);
result.Add(prod);
}
}
while (reader.NextResult());
Note outer do .. while loop since we always have at least one result set.
You use NextResult which advances the reader to the next result set. This makes sense if you have multiple sql queries and you'd use it after the while-loop. Here it's just unnecessary and wrong.
You are already advancing the reader to the next record with Read.
If I get rid of it, this error occur : Unable to cast object of type
'System.DBNull' to type 'System.String.
You can use IsDBNull:
int nameIndex = reader.GetOrdinal("Name");
string name = reader.IsDBNull(nameIndex) ? null : reader.GetString(nameIndex);
int descIndex = reader.GetOrdinal("Description");
string description = reader.IsDBNull(descIndex) ? null : reader.GetString(descIndex);
var prod = new Product((int)reader["ID"],
name,
(double)reader["Price"],
(int)reader["Stock"],
(int)reader["VATID"],
description);
Use it for every nullable column, for the numeric columns you could use nullable types like int?.
You have an error in your code:
Remove the line reader.NextResult();
NextResult is used for moving to next result set not next record.
Definitely remove the NextResult(). That does NOT move between individual records in the same query. Read() does this for you already. Rather, NextResult() allows you to include multiple queries in the same CommandText and run them all in one trip to the database.
Try this:
public IEnumerable<Product> Search(string name)
{
using (var conn = new SqlConnection(connectionString))
using (var command = new SqlCommand("SELECT * FROM Product ", conn))
{
if (!string.IsNullOrEmpty(name) )
{
command.CommandText += " WHERE Name LIKE #Name + '%'";
command.Parameters.Add("#Name", SqlDbType.NVarChar, 50).Value = name;
}
conn.Open();
using var reader = command.ExecuteReader();
{
while (reader.Read())
{
var prod = new Product((int)reader["ID"], reader["Name"].ToString(),
(double)reader["Price"], (int)reader["Stock"], (int)reader["VATID"],
reader["Description"].ToString());
yield return prod;
}
}
}
}

How to return a data from while c# using oracle 11g

I need to return all values from my table. How to write a code inside while?
var stringConnection = "Data Source = X; User Id = X; Password = X";
var sql = "SELECT * FROM TABLE";
OracleConnection _oracleConnection = new OracleConnection(stringConnection);
_oracleConnection.Open();
OracleCommand cmd = new OracleCommand(sql, _oracleConnection);
var dr = cmd.ExecuteReader();
var list = new List<dynamic>();
while(dr.Read())
{
// my doubt is here
}
return list;
var stringConnection = "Data Source = X; User Id = X; Password = X";
var sql = "SELECT * FROM TABLE";
OracleConnection _oracleConnection = new OracleConnection(stringConnection);
_oracleConnection.Open();
OracleCommand cmd = new OracleCommand(sql, _oracleConnection);
var dr = cmd.ExecuteReader();
var list = new List<dynamic>();
while(dr.Read())
{
// **** read column name data from table ****
string Id = (string)dr["Id"];
string company = (string)dr["company"];
string city = (string)dr["City"];
var objItem = new { Id = Id, company = company, city = "city" };
list.Add(objItem);
}
return list;
You have several options. 1 - load DataSet from OracleDataReader. There you will have all your data.
2 - you can still use select *... but you need a model. Then create List<SomeModel> instead of List<dynamic> with
while (reader.Read())
{
model.Property = reader["columnName"]; // will need convert type and take care of DB null. Can use existing extnsions
. . . .
}
3 - For arbitrary number of columns use OracleDataReader.FieldCount and some storage like List<object[]>
var data = new List<object[]>();
var fCnt = reader.FieldCount;
while (reader.Read())
{
var arr = new Object[fCnt];
for(int i = 0; i < fCnt; i++)
arr[i] = reader[i];
data.Add(arr);
}
The unfortunate part with #3 is that in the end you can get jagged array and not 2-dimentional one but you now have enough info to convert it. But I don't remember when I needed to do #3. So think about #1 and #2
And one more thing - absolutely no need for dynamic here. Stay away.

Querying data from two databases in C#

I currently have a query where I am getting data from 2 different databases which is getting appended to one list called "accountbuys".
My first table has three data entry(3 accounts which want to buy stocks
The next table with 17 data points(17 stocks to buy)
I am merging these tables and getting output
However,the output I want , should have 17 data points repeated each time for the 3 different accounts, so that the end user can drill down and buy whichever stock he wants
PS: If what I am trying to ask is not clear, please be polite and tell me so. Please try not to be rude, I am still learning and new to StackExchange!
This is my code :`
public List<BuySellModel> GetBuyDataWthAccount()
{
List<BuySellModel> accountbuys = new List<BuySellModel>();
using (SqlConnection connectionreit = new SqlConnection(HubConnection))
using (SqlConnection connection = new SqlConnection(PMConnection))
{
connectionhub.Open();
connection.Open();
SqlCommand command3 = new SqlCommand(#"SELECT distinct(table1.name) as 'Symbol' ,table2.Segment as 'Segment',table2.AllocationAmount as 'AllocationAmount',table2.PX_LAST as 'Price',
table1.CUR_MKT_CAP as 'CMC',table1.FCFY_WITH_CUR_MKT_CAP as 'FCMC',table1.ROIC as 'ROIC', table1.ROICDELTA as 'ROICD' FROM View_REIT_Model_And_Holdings as table1
INNER JOIN [MostRecentlyInModelSelected] as table2
ON table1.name = table2.Ticker
WHERE table1.AllocationAmount != -1 AND
NOT EXISTS (SELECT NULL FROM [ViewPCData] as table3 WHERE table1.name = table3.Symbol AND table2.Segment = table3.SubsectorDescription AND table3.Objective = 'REITS' AND table3.SectorDescription != 'NULL' AND table3.SubsectorDescription != 'NULL')",
connectionreit);
command3.CommandType = CommandType.Text;
SqlCommand command4 = new SqlCommand("SELECT PortfolioAccountNumber, PortfolioDescription, SUM(TotalValue) as 'TotalValue' FROM [ViewPCData] WHERE Objective = 'REITS' GROUP BY PortfolioAccountNumber,PortfolioDescription", connection);
command4.CommandType = CommandType.Text;
var reader = command3.ExecuteReader();
var reader1 = command4.ExecuteReader();
if (reader1.HasRows)
{
while (reader1.Read())
{
BuySellModel accountb = new BuySellModel();
accountb.PortfolioAccount = reader1.GetString(reader1.GetOrdinal("PortfolioAccountNumber"));
accountb.PortfolioDescription = reader1.GetString(reader1.GetOrdinal("PortfolioDescription"));
accountb.AccountAmount = reader1.GetDecimal(reader1.GetOrdinal("TotalValue"));
accountbuys.Add(accountb);
if (reader.HasRows)
{
//foreach(var account in accountbuys)
//{
while (reader.Read())
{
BuySellModel buy = new BuySellModel();
buy.Symbol = reader.GetString(reader.GetOrdinal("Symbol"));
buy.Segment = reader.GetString(reader.GetOrdinal("Segment"));
//if (accountNumber == "soand os")
//{
// 1/3 of totalaccountvalue
buy.AllocationAmount = (reader.GetDouble(reader.GetOrdinal("AllocationAmount")));
//}
buy.Price = reader.GetDouble(reader.GetOrdinal("Price"));
buy.MarketValue = reader.GetDouble(reader.GetOrdinal("CMC"));
buy.FCFY = reader.GetDouble(reader.GetOrdinal("FCMC"));
buy.ROIC = reader.GetDouble(reader.GetOrdinal("ROIC"));
buy.ROICdelta = reader.GetDouble(reader.GetOrdinal("ROICD"));
buy.Buy = true;
//account1 = account.accountnumber;
accountbuys.Add(buy);
}
//} //for loop
}
} // accounts
} //reader1.hasrows
connectionhub.Close();
connection.Close();
}
return accountbuys;
}
EDIT:
Split the tables into two different lists and merged them later. This is working well now. Seems good for scaling too.
public List<BuySellModel> GetBuyDataWthAccount()
{
List<BuySellModel> accountbuys = new List<BuySellModel>();
List<Account> accounts = new List<Account>();
using (SqlConnection connection = new SqlConnection(PMConnection))
{
connection.Open();
SqlCommand command3 = new SqlCommand(#"SELECT distinct(table1.name) as 'Symbol' ,table2.Segment as 'Segment',table2.AllocationAmount as 'AllocationAmount',table2.PX_LAST as 'Price',
table1.CUR_MKT_CAP as 'CMC',table1.FCFY_WITH_CUR_MKT_CAP as 'FCMC',table1.ROIC as 'ROIC', table1.ROICDELTA as 'ROICD' FROM View_Model_And_Holdings as table1
INNER JOIN [MostRecentlyInModelSelected] as table2
ON table1.name = table2.Ticker
WHERE table1.AllocationAmount != -1 AND
NOT EXISTS (SELECT NULL FROM [ViewPCData] as table3 WHERE table1.name = table3.Symbol AND table2.Segment = table3.SubsectorDescription AND table3.Objective = 'STOCKS' AND table3.SectorDescription != 'NULL' AND table3.SubsectorDescription != 'NULL')",
connectionreit);
command3.CommandType = CommandType.Text;
SqlCommand command4 = new SqlCommand("SELECT PortfolioDetail , SUM(TotalValue) as 'TotalValue' FROM [ViewPCData] WHERE Objective = 'STOCKS' GROUP BY PortfolioAccountNumber,PortfolioDescription", connection);
command4.CommandType = CommandType.Text;
var reader = command3.ExecuteReader();
var reader1 = command4.ExecuteReader();
if (reader1.HasRows)
{
while (reader1.Read())
{
Account accountb = new Account();
accountb.PortfolioDetail = reader1.GetString(reader1.GetOrdinal("PortfolioDetail"));
// accountb.PortfolioDescription = reader1.GetString(reader1.GetOrdinal("PortfolioDescription"));
accountb.AccountAmount = reader1.GetDecimal(reader1.GetOrdinal("TotalValue"));
accounts.Add(accountb);
}
}
//List<BuyReits> buys = new List<BuyReits>();
if (reader.HasRows && accounts.Count > 0)
{
while (reader.Read())
{
foreach (var acc in accounts)
{
BuySellModel buy = new BuySellModel();
buy.Symbol = reader.GetString(reader.GetOrdinal("Symbol"));
buy.Segment = reader.GetString(reader.GetOrdinal("Segment"));
buy.AllocationAmount = (reader.GetDouble(reader.GetOrdinal("AllocationAmount")));
buy.Price = reader.GetDouble(reader.GetOrdinal("Price"));
//buy.Quantity = reader.GetInt32((reader.GetOrdinal("AllocationAmount"))/(reader.GetOrdinal("Price")));
buy.MarketValue = reader.GetDouble(reader.GetOrdinal("CMC"));
buy.FCFY = reader.GetDouble(reader.GetOrdinal("FCMC"));
buy.ROIC = reader.GetDouble(reader.GetOrdinal("ROIC"));
buy.ROICdelta = reader.GetDouble(reader.GetOrdinal("ROICD"));
buy.Buy = true;
buy.PortfolioAccount = acc.PortfolioDetail;
buy.AccountAmount = acc.AccountAmount;
accountbuys.Add(buy);
}
}
}
connection.Close();
}
return accountbuys;
}
The following provides the cross join in the C# layer (not saying it is the best possible solution, but it gets you closer to ready):
using (SqlConnection connectionhub = new SqlConnection(HubConnection))
using (SqlConnection connection = new SqlConnection(PMConnection))
{
connectionhub.Open();
connection.Open();
SqlCommand command3 = new SqlCommand(#"
SELECT distinct(table1.name) as 'Symbol',
table2.Segment as 'Segment',
table2.AllocationAmount as 'AllocationAmount',
table2.PX_LAST as 'Price',
table1.CUR_MKT_CAP as 'CMC',
table1.FCFY_WITH_CUR_MKT_CAP as 'FCMC',
table1.ROIC as 'ROIC',
table1.ROICDELTA as 'ROICD'
FROM View_REIT_Model_And_Holdings as table1
INNER JOIN [MostRecentlyInModelSelected] as table2
ON table1.name = table2.Ticker
WHERE table1.AllocationAmount != -1
AND NOT EXISTS (SELECT NULL
FROM [ViewPCData] as table3
WHERE table1.name = table3.Symbol
AND table2.Segment = table3.SubsectorDescription
AND table3.Objective = 'REITS'
AND table3.SectorDescription != 'NULL'
AND table3.SubsectorDescription != 'NULL')",
connectionreit);
command3.CommandType = CommandType.Text;
SqlCommand command4 = new SqlCommand(#"
SELECT PortfolioAccountNumber,
PortfolioDescription,
SUM(TotalValue) as 'TotalValue'
FROM [ViewPCData]
WHERE Objective = 'REITS'
GROUP BY PortfolioAccountNumber, PortfolioDescription", connection);
command4.CommandType = CommandType.Text;
var stocksDS = new DataSet();
var stocksDA = new System.Data.SqlClient.SqlDataAdapter();
stocksDA.SelectCommand = command3
stocksDA.Fill(stocksDS, "stocks");
var acctsDS = new DataSet();
var acctsDA = new System.Data.SqlClient.SqlDataAdapter();
acctsDA.SelectCommand = command4
acctsDA.Fill(acctsDS, "accts");
var stocks = stocksDS.Tables["stocks"].AsEnumerable();
var accts = acctsDS.Tables["accts"].AsEnumerable();
var results = (from stocksDR in stocks
from acctsDR in accts
select new BuySellModel {
PortfolioAccount = acctsDR["PortfolioAccountNumber"],
PortfolioDescription = acctsDR["PortfolioAccountDescription"],
AccountAmount = acctsDR["TotalValue"],
Symbol = stocksDR["Symbol"],
Segment = stocksDR["Segment"],
AllocationAmount = stocksDR["AllocationAmount"],
Price = stocksDR["Price"],
MarketValue = stocksDR["CMC"],
FCFY = stocksDR["FCMC"],
ROIC = stocksDR["ROIC"],
ROICdelta = stocksDR["ROICD"],
Buy = true
});
foreach (BySellModel buy in results) {
accountBuys.Add(buy);
}
connectionhub.Close();
connection.Close();
}
Edit: removed offending parentheses.
Without changing any of your C# code, it may be possible to get what you want by changing the SQL query from an INNER JOIN query to a CROSS JOIN query.
Some caveats regarding this approach though:
If you include a WHERE clause, the query will act as an INNER JOIN.
Cross joins can be significantly slower, as the query is looking to make all possible combinations. With only 51 (3 * 17) combinations available that will not be a significant issue, but if you are looking to scale this to more clients and stocks, it will become increasingly worse performance wise

Update database column during the reading of rows

I need to change a column value in the table of my database every time I read a row. Specifically, after the reading of the row I must set the column SENT=1 (by default, before the reading, the value is 0).
My code is:
var sqlCommand = "SELECT * FROM detector_output WHERE SENT=0";
var Command = new MySqlCommand(sqlCommand, connection);
MySqlDataReader reader = Command.ExecuteReader();
while (reader.Read())
{
var ident = reader.GetString(0);
var SENSOR_TYPE = reader.GetString(1);
var MSG_NAME = reader.GetString(2);
var DATA_NAME = reader.GetString(3);
var VALUE = reader.GetString(4);
var TIMESTAMP = reader.GetString(5);
var connectionUP = new MySqlConnection(cs);
connectionUP.Open();
var sqlCommandUPDATE = "UPDATE detector_output SET SENT=1 WHERE ID=ident";
var CommandUpdate = new MySqlCommand(sqlCommandUPDATE, connectionUP);
CommandUpdate.ExecuteReader();
The error that I have encountered is:
unknown column 'ident' in where clause ...
You don't use ident as a variable in your query. You defined it as ID=ident that's why your provider confuse and it thinks you actually try to filter where your ID column value is equal to ident column value.
What if ident is a character? Then, it would be defined as ID = 'ident', isn't it?
Define a parameter for it and later, add it's value.
var sqlCommandUPDATE = "UPDATE detector_output SET SENT=1 WHERE ID = #ident";
var CommandUpdate = new MySqlCommand(sqlCommandUPDATE, connectionUP);
CommandUpdate.Parameters.AddWithValue("#ident", ident);
Also use using statement to dispose your connections, commands and readers.

Categories