Get Multiple Rows on SQLite in Flutter - database

I keep only the "productID" information of the products added to the favorites in the "favorite" table in the database. I had no problem adding this information to the table. (product table and favorite table are separate.) But when I wanted to list all favorite products in the table, I found the codes that allowed me to query one by one. What I need is a function that can give the List<int> productIDs as parameter and return favorite products as List<Map<String, dynamic>>> . I looked almost everywhere but could not find it.
The function I use to fetch favorite product IDs stored in the favorite table:
Future<List<Map<String, dynamic>>> favProds() async{
List<int> IDs = new List<int>();
var db = await _getDatabase();
var result = await db.query(_favTable, orderBy: '$_columnProdID DESC');
for(Map incomingMap in result){
IDs.add(incomingMap['prodID']);
}
return _getFavProdsWithIDList(IDs);
}
This is my function that takes the ID List as a parameter and returns a list containing favorite products as maps:
Future<List<Map<String, dynamic>>> _getFavProdsWithIDList(List<int> IDs) async{
var db = await _getDatabase();
var result = await db.query("$_prodTable", where: '$_columnProdID = ?', whereArgs: IDs);
return result;
}
Here is the error I get when I use them:
Unhandled Exception: DatabaseException(Cannot bind argument at index 8 because the index is out of range. The statement has 1 parameters.) sql 'SELECT * FROM urun WHERE urunID = ?' args [230, 180, 179, 20, 19, 18, 17, 2]}
From this error, we see that adding products to the favorite table is successful.

If you wan select records where id in list of ids you should use query like
SELECT * FROM urun WHERE urunID IN (1, 2, 3);
You have two options.
Provide same number of placeholders as list length
final placeholders = List.generate(5,(_) => "?").join(",");
var result = await db.query("$_prodTable", where: '$_columnProdID IN ($placeholders)', whereArgs: IDs);
Since ids is integers just
var result = await db.query("$_prodTable", where: '$_columnProdID IN (${IDs.join(",")})');

Related

How to avoid adding duplicate data from CSV file to SQL Server database. Using CSVHelper and C# Blazor

I have my database table named 'JobInfos' in SQL Server which contains many columns.
JobID - (int) auto populates incrementing value when data added
OrgCode - (string)
OrderNumber - (int)
WorkOrder - (int)
Customer - (string)
BaseModelItem - (string)
OrdQty - (int)
PromiseDate - (string)
LineType -(string)
This table gets written to many times a day using a Blazor application with Entity Framework and CSVHelper. This works perfectly. All rows from the CSV file are added to the database.
if (fileExist)
{
using (var reader = new StreamReader(#path))
using (var csv = new CsvReader(reader, config))
{
var records = csv.GetRecords<CsvRow>().Select(row => new JobInfo()
{
OrgCode = row.OrgCode,
OrderNumber = row.OrderNumber,
WorkOrder = row.WorkOrder,
Customer = row.Customer,
BaseModelItem = row.BaseModelItem,
OrdQty = row.OrdQty,
PromiseDate = row.PromiseDate,
LineType = row.LineType,
});
using (var db = new ApplicationDbContext())
{
while (!reader.EndOfStream)
{
if (lineNumber != 0)
{
db.AddRange(records.ToList());
db.SaveChanges();
}
lineNumber++;
}
NavigationManager.NavigateTo("/", true);
}
}
As these multiple CSV files can contain rows that may already be in the database table, I am getting duplicate records when the table is read from, which causes the users to delete all the newer duplicate rows manually to only keep the original entry.
I have no control over the CSV files or their creation. I am trying to only add rows that contain new data based on the WorkOrder number which can not be the same as any others.
I found another post here on StackOverflow which helps but I am stuck with a remaining error I can't figure out.
The Helpful post
I changed my code here...
if (lineNumber != 0)
{
var recordworkorder = records.Select(x => x.WorkOrder).ToList();
var workordersindb = db.JobInfos.Where(x => recordworkorder.Contains(x.WorkOrder)).ToList();
var workordersNotindb = records.Where(x => !workordersindb.Contains(x.WorkOrder));
db.AddRange(records.ToList(workordersNotindb));
db.SaveChanges();
}
but this line...
var workordersNotindb = records.Where(x => !workordersindb.Contains(x.WorkOrder));`
throws an error at the end (x.WorkOrder) - CS1503 Argument 1: cannot convert from 'int' to 'DepotQ4.Data.JobInfo'
WorkOrder is an int
JobID is the Primary Key and an int
Every record in the table must have a unique WorkOrder
I am not sure what I am not seeing. Could use some help here please?
Your variable workordersindb is a List<JobInfo>. So when you try to select from records.Where(x => !workordersindb.Contains(x.WorkOrder)) you are trying to match the list of JobInfo in workordersindb to the int of x.WorkOrder. workordersindb needs to be a List<int> in order to be able to use it with the Contains. records would have had the same issue, but you solved it by creating the variable recordworkorder and using records.Select(x => x.WorkOrder) to get a List<int>.
if (lineNumber != 0)
{
var recordworkorder = records.Select(x => x.WorkOrder).ToList();
var workordersindb = db.JobInfos.Where(x => recordworkorder.Contains(x.WorkOrder)).Select(x => x.WorkOrder).ToList();
var workordersNotindb = records.Where(x => !workordersindb.Contains(x.WorkOrder));
db.JobInfos.AddRange(workordersNotindb);
db.SaveChanges();
}

Get only dataValues from sequelize raw query instead of Model instance

I'm using sequelize and using raw query to get the datas from table. But I'm getting all of the model instances while I only need the dataValues.
My setup looks like this:
const sequelize = new Sequelize({
database: process.env.PGDATABASE,
username: process.env.PGUSER,
password: process.env.PGPASS,
host: process.env.PGHOST,
port: process.env.PGPORT,
dialect: "postgres"
});
getPostGres: () => {
return sequelize;
}
and the way I'm querying the database looks like this:
let messageRatingsArr = await getPostGres().query(
`SELECT mr.support_email, mr.support_name,
(select count(mrn."chatId") as total FROM message_ratings as mrn WHERE mrn."ratingType"='NEGATIVE' and mr.support_email = mrn.support_email) as negative,
(select count(mrp."chatId") as total FROM message_ratings as mrp WHERE mrp."ratingType"='POSITIVE' and mr.support_email = mrp.support_email) as positive,
(select count(mrm."chatId") as total FROM message_ratings as mrm WHERE mrm."ratingType"='MIXED' and mr.support_email = mrm.support_email) as mixed,
(select count(mru."chatId") as total FROM message_ratings as mru WHERE mru."ratingType"='NEUTRAL' and mr.support_email = mru.support_email) as neutral
FROM message_ratings mr
WHERE mr."createdAt" >= '${properFromDate}' AND mr."createdAt" <= '${properToDate}'
group by mr.support_email, mr.support_name
limit ${args.count} offset ${args.offset} `,
{
model: MessageRatingPG,
mapToModel: true
}
);
let messageRatings = messageRatingsArr.map(item=>{
return item.dataValues;
})
let result = connectionFromArray(messageRatings, args);
result.totalCount = messageRatings.length;
return result;
As you can see, since I'm mapping the data from the query which has all kinds of stuff like dataValues, _options, isNewRecord etc., looping through the array if I have a large data set isn't efficient, so what can I do to only get the dataValues?
From https://sequelize.org/master/manual/raw-queries.html:
In cases where you don't need to access the metadata you can pass in a query type to tell sequelize how to format the results. For example, for a simple select query you could do:
sequelize.query("SELECT * FROM `users`", { type: sequelize.QueryTypes.SELECT})
.then(users => {
// We don't need spread here, since only the results will be returned for select queries
})
Now, looking at your code, and comparing with the next paragraph in the docs:
A second option is the model. If you pass a model the returned data will be instances of that model.
// Callee is the model definition. This allows you to easily map a query to a predefined model
sequelize
.query('SELECT * FROM projects', {
model: Projects,
mapToModel: true // pass true here if you have any mapped fields
})
.then(projects => {
// Each record will now be an instance of Project
})
I'd suggest removing from your original code, the following:
{
model: MessageRatingPG,
mapToModel: true
}
and replacing it with { type: sequelize.QueryTypes.SELECT }
You have to add to your query the attribute raw. From the docs
Sometimes you might be expecting a massive dataset that you just want
to display, without manipulation. For each row you select, Sequelize
creates an instance with functions for update, delete, get
associations etc. If you have thousands of rows, this might take some
time. If you only need the raw data and don't want to update anything,
you can do like this to get the raw data.
Project.findAll({ where: { ... }, raw: true })

Entity Framework join on large in-memory list

From what I am aware doing a join on in-memory data basically loads the entire data set in, e.g.
var ids = new int[] { 1, 2, 3}
var data = context.Set<Product>().Join(ids, x => x.Id, id => id, (x, id) => x)
This will cause the entire 'Product' table to be loaded into memory. In my current case this is not an option as it contains millions of records.
The common solution I have seen is to use Contains(), e.g.
var ids = new int[] { 1, 2, 3}
var data = context.Set<Product>().Where(x => ids.Contains(x.Id));
which generates an SQL IN clause like WHERE Id IN (1, 2, 3)
But what if the in-memory list is very large? Does this create any issues if say you have an IN clause with 1000's of values? or 10's of thousands?
Is there some alternative query that creates different (better?) SQL?

how to aggregate records in a list

I have a batch program that I need to aggregate (rollup) several currency fields by a specific contact and fund. I need a fresh set of eyes on this as I can't figure out how I need to correctly rollup the fields by the contact and fund.
Here is my current code in the batch program:
for (My_SObject__c obj : (List<My_SObject__c>)scope) {
if(!dbrToContactMap.isEmpty() && dbrToContactMap.size() > 0) {
if(dbrToContactMap.containsKey(obj.DBR__c)) {
List<Id> contactIds = dbrToContactMap.get(obj.DBR__c);
for(Id contactId : contactIds) {
My_Rollup__c rollup = new My_Rollup__c();
rollup.Fund_Name__c = obj.FundName__r.Name;
rollup.DBR__c = obj.DBR__c;
rollup.Contact__c = contactId;
rollup.YearToDate__c = obj.YearToDate__c;
rollup.PriorYear__c = obj.PriorYear__c;
rollupsToInsert.add(rollup);
}
}
}
}
if(!rollupsToInsert.isEmpty() && rollupsToInsert.size() > 0) {
insert rollupsToInsert;
}
I'm iterating over the scope of records, which is about 275,000 records that are returned. I have a map that returns a list of contacts by the key field in my scope.
Now, currently, as I loop over each contact, I put those into a list and insert. The problem is I need to aggregate (rollup) by fund and contact.
For example, let's say the map returns the contact for "John Doe" ten times for the specific key field. So, there are going to be 10 related records on "John Doe's" contact record.
The problem is that the same fund can be in those 10 records, which need to be aggregated.
So, if "Fund A" shows up 5 times and "Fund B" shows up 3 times and "Fund C" shows up 2 times, then I should only have 3 related records in total on the "John Doe" contact record that are aggregated (rolled up) by the fund.
I haven't been able to figure out how to do the rollup by fund in my list.
Can anyone help?
Any help is appreciated.
Thanks.
The key here is to use a stringified index key for your map, instead of using a raw Sobject ID. Think of this as a composite foreign key, where the combined values from a Fund and a DBR are joined. Here is the basic idea:
Map<String, My_Rollup__c> rollupMap = new Map<String, My_Rollup__c>();
for (My_SObject__c obj : (List<My_SObject__c>) scope) {
// .. stuff ..
String index = '' + new String[] {
'' + obj.FundName__r.Id,
'' + obj.DBR__c,
'' + contactId
};
// Find existing rollup, or create new rollup object...
My_Rollup__c rollup = rollupMap.get(index);
if (rollup == null) {
rollup = new My_Rollup__c();
rollupMap.put(index, rollup);
}
// Aggregate values..
}
// And save
if (rollupMap.isEmpty() == false) {
insert rollupMap.values();
}
The result is that you combining all the different "keys" that makeup a unique rollup record into a single stringified key, and then using that stringified key as the index in the map to enforce uniqueness.
The example above is incomplete, but you should be able to take it from here.

Joining tables and comparing IDs in SQL Server

I have tried everything but this is just not working, I am grabbing the ID from one database (RMS) and comparing that ID to another database called RMSposts. I want it to look like the picture when for example:
TABLE RMS
ID: 100
TABLE RMSposts
PostID:100
Notes: sdsdfgsdfg
PostID: 100
Notes: dsfgsdfg
PostID: 99
Notes: blabla
So if I click on case 100 only the Notes for case 100 show
This code only works when I plug in the number manually, but I want to use razor syntax to call up the ID of the case I am looking at. Could someone help please? I have not been able to figure this out!
#{
string IDp=Request["ID"];
var query = ("SELECT * FROM RMSposts WHERE PostID=#0");
var data2 = db.QuerySingle(query, UrlData[0]);
{foreach(var row in db.Query("Select * FROM RMSposts WHERE PostID=100")){
if (data2 != null) {
#row.Notes<br>
#Name<br>
#row.Updated<Br><hr>
}else {
<div>----There are no previous entries----</div><br>
}
}}}
Maybe I haven't understood what you want to accomplish.
The following code should display your data:
#{
string param = (string.IsNullOrEmpty(UrlData[0]) ? "0": UrlData[0]);
var db = Database.Open("yourDb");
var query = "SELECT * FROM RMSposts WHERE PostID = #0";
var data = db.Query(query, param);
}
<div>
#if (data != null)
{
foreach(var row in data)
{
<p>#row.Notes<br>#Name<br>#row.Updated<br><hr></p>
}
} else {
<div>----There are no previous entries----</div><br>
}
</div>

Resources