ServiceStack OrmLite OrderBy on joined table columns - ormlite-servicestack

I wish to make an OrderBy statement in OrmLite, using data from multiple joined tables in my query:
myQuery.OrderBy<MainTable, SubTable>((m, s) => m.Col1 < s.Col2)
just as you can with OrmLite's WHERE clauses:
myQuery.Where<MainTable, SubTable>((m, s) => m.Col1 < s.Col2)
But it is not possible with OrderBy. Can I work around this issue in an alternative way?

Use OrderBy and ThenBy to order by multiple columns, e.g:
q.OrderBy<MainTable>(m => m.Col1)
.ThenBy<SubTable>(s => s.Col2);
If you need to OrderBy an expression you can use a string:
q.OrderBy("Col1 < Col2");
For typed properties in string expressions use:
q.OrderBy($"{q.Column<MainTable>(m => m.Col1)} < {q.Column<SubTable>(s => s.Col2)}");
In the latest v5.5.1 that's now available on MyGet you will also be able to use a typed expression from multiple tables in your OrderBy, e.g:
q.OrderBy<MainTable, SubTable>((m, s) => m.Col1 < s.Col2 ? m.Col1 : s.Col2)

Related

Nette - database INSERT - number of affected rows

I would like to find out the number of affected (inserted) rows after inserting into the table. I didn't figure out how to do it in the documentation. The update returns the number of affected rows. The insert returns Nette\Database\Table\ActiveRow
How do I get it?
$affected = $context->table('author')->insert([
[
'name' => 'Sansa Stark',
'born' => null
], [
'name' => 'Arya Stark',
'born' => null
]
]);
bdump($affected); // Nette\Database\Table\ActiveRow - I need the number of inserted records
Nette Database Explorer doesn't return count after insert(). It is not useful information as long as you can count data before insert by yourself.
$data = [...];
$count = count($data);
$context->table('author')->insert($data);
It works only with update and delete as is mentioned in documentation.
$count = $context->table('author')
->where('id', 10)
->delete();
It might be possible with getRowCount() over query in Nette Database Core
Nette Database Core is built upon PDO. Alas, the authors tend to create their own objects instead of extending PDO, which makes such elementary operations tedious:
// get Nette ResultSet object
$resultSet = $this->database->query("INSERT/UPDATE/DELETE...");
// get original PDOStatement object
$pdoStatement = $resultSet->getPdoStatement();
// get the affected rows from PDO object (instead of $resultSet->rowCount())
$pdoStatement->rowCount();
A word of warning for those considering Nette for production: There is no real documentation, only cook books and autogenerated PHPDoc which just prints names without any explanation.

Is there a way to select a columns from a joined table without explicitly listing all columns?

I'm trying to use JoinSqlBuilder to select a data from one of the joined tables, and can't find a way to do that unless I list all columns from that table. Hopefully I'm missing something and it actually can be done.
This is approximately what I have:
var sql = new JoinSqlBuilder<Product, Product>()
.Join<Product, Customer>(src => src.Id, dst => dst.Id)
.Where<Customer>(x => x.Id == Id);
and I want to select everything from a product table. The query above throws an exception complaining about column name collisions, so its clearly does a select from both tables.
Edit: In the end I want to have this sql (never mind the design, its not a real thing):
select
p.* //<-- This is the piece that I'm struggling with
from product p inner join customer c on p.id on c.productId
where blah;
Looks like OrmLite want me to explicitly list all columns I want to return, which I want to avoid.
Note: I'm using 3.9.71 of servicestack. I've not looked at the 4.0 implementation yet.
I think you have a FK relationship problem with your join. Assuming that a product has a customer FK named (CustID), it'd look like this. Additionally, you'd need a POCO to represent the result set, if you are returning a "combination" of the results. I don't think you'll want to return both "ID" columns, and instead return a FK column.
return _factory.Run<ProductCustomer>(conn=>
{
var jn = new JoinSqlBuilder<Product, Customer>();
jn = jn.Join<Product, Customer>(srcProd => srcProd.CustId,
dstCust => dstCust.Id, // set up join Customer.id -> Product.CustId
p => new { p.Id, p.OtherField}, // product table fields returned
c => new { c.Name, c.AddressId}, // customer fields returned
null, //where clause on the product table
cust=>cust.Id = customerId // where clause on the customer table
);
var sql = jn.ToSQL();
return conn.FirstOrDefault<ProductCustomer>(sql);
}
Hope this helps.
Edit: After your Edit, try this:
// set up join Customer.id -> c.ProductId
jn = jn.Join<Product, Customer>(srcProd => srcProd.Id, dstCust => dstCust.productId)
.Where<Customer>(c=>c.Id == custIdParameter);
var sql = jn.ToSql();
You can add a ".Where" again for the
Where<Product>(p=>p.id == foo);
if you need to add more product with your BLAH. This should get you close.
Have you tried the SelectAll extension method?
var sql = new JoinSqlBuilder<Product, Product>()
.Join<Product, Customer>(src => src.Id, dst => dst.Id)
.SelectAll<Product>()
.Where<Customer>(x => x.Id == Id);

select single or multiple records using a single linq query

I want to pass a valid id or -1("ALL") from a drop down to the function.
Can I select 'All' rows or one row based on the selected option from ddl from a table using a single linq query?
You can add an OR to the statement to get the result. Basically, (#value = -1) OR (id = #value). Whenever value is -1, it will return all value, otherwise that we be false and only return the matching record.
Having an OR in the LINQ can sometimes result in full-table scans and non-optimal retrievals.
Instead of having a condition inside your LINQ statement, I'd recommend building your LINQ query conditionally based on whether you want a single result or "all":
// Initialize your select
var query = db.Select(r => r);
// Conditionally add the "where"
if (selectedValue != -1)
query = query.Where(n => n.id == selectedValue);
// Collect your results
foreach (var record in query)
{
// ...
}
If you want all records, then your query won't include a where clause at all, otherwise, the where clause will include only the filter by id.

How to do a primary key to equal to an array of int[] in LINQ?

I am trying to select values where the primary ID which is an int is not equal to any ints in an array int[].
Here is my current code:
Where(c => !c.CountryId.Equals(model.CampaignCountryIds))
model.CampaignCountryIds is of type int[]?
Is there any easy way to do this? This error is stemming from that:
Comparison operators not supported for type 'System.Nullable`1[System.Int32][]'.
Thanks,
Darren
Use Contains:
.Where(c => !model.CampaignCountryIds.Contains(c.CountyId))

Aggregate LINQ results

If I have a Publisher table that has many Books, and each Book can have many Authors, how do I get a list of distinct Authors for a Publisher? In SQL you would just join all the tables, select the Author, and use SELECT DISTINCT. Using LINQ I end up with an IEnumerable(Of EntitySet(of Author)):
Dim temp = From p in Publishers Select (From b in p.Books Select b.Author)
and this still doesn't address duplicate Authors.
Is there a way to get a flat list of Authors, ungrouped from the Books, in a single query? I know I could loop through the sets and create one list and use Distinct on that. I was curious if it could be done in one statement.
Here's the original query.
var authorSets = Publishers
.Where(...)
.Select(p => p.Books.Select(b => b.Author));
And here is the same query improved by SelectMany to flatten the hierarchy.
var authors = Publishers
.Where(...)
.SelectMany(p => p.Books.Select(b => b.Author))
.Distinct();
For more information: MSDN
Would something like this work:
In C#:
var publisherAuthors = Authors.Where(a => a.Books.Where(b => b.Publisher.Name.Equals("Some Publisher")));
If you are more comfortable with SQL than lambda expression, try:
from b in Books
join ba in BookAuthorIndexes on b.BookId equals ba.BookId
join a in Authors on ba.AuthorId equals a.AuthorId
where b.Publisher.Name = "foo"
select a distinct
I'm making some pretty big assumptions about your schema here, but something along those lines.

Resources