Distinct query in Criteria NHibernate - distinct

I need the corresponding query in Criteria language to this one (to retrieve all categories from my table but to distinct them):
SELECT DISTINCT categoryName
FROM Category
WHERE CategoryID IN (
SELECT CategoryID
FROM FoodCategory
)
ORDER BY categoryName
I have table FoodCategory table
id | FoodID | CategoryID
--------|---------------|------------
| |
| |
| |
Actually CategoryID is a foreign key that is pointing to this table here. This is table for Category:
CategoryID | categoryName | otherField
---------------|------------------|------------
| |
| |
| |
And this is table for Food:
FoodID | FoodName | otherField
---------------|------------------|------------
| |
| |
| |

Something like that should do the trick :
public List<String> retrieveFoodCategoryNames() {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<String> criteriaQuery = builder.createQuery(String.class);
Root<FoodCategory> root = criteriaQuery.from(FoodCategory.class);
// by default an inner join so it will only get the categories which have their id in the FoodCategory table
Join<FoodCategory, Category> joinCategory = root.join(FoodCategory_.category);
Fetch<FoodCategory, Category> fetchCategory = root.fetch(FoodCategory_.category);
Path<String> categoryNamePath = fetchCategory.get(Category_.categoryName);
criteriaQuery.select(categoryNamePath).distinct(true);
criteriaQuery.orderBy(builder.asc(categoryNamePath));
return entityManager.createQuery(criteriaQuery).getResultList();
}
This is not the exact same SQL request because you used a subquery where I'm using a join but it seemed more suited to this particular case. The subquery syntax is a bit more complex and I will not try to write it without compiling! ^^
If something is unclear let me know :-)

Related

Use UNION with DISTINCT in Laravel Eloquent query builder with relations

I have 2 tables,
properties
+----+-----------+
| id | parent_id |
+----+-----------+
| 1 | null |
| 2 | 1 |
| 3 | null |
| 4 | 3 |
| 5 | 3 |
| 6 | null |
+----+-----------+
and
sale_services
+----+-------------+------+
| id | property_id | rank |
+----+-------------+------+
| 1 | 2 | 5 |
| 2 | 4 | 4 |
| 3 | 5 | 6 |
| 4 | 6 | 7 |
+----+-------------+------+
and corresponding Eloquent Models (SaleService and Property) linked to each other by relation (sale_service.property_id = property.id). Property can be linked to another Property in the same table.
I need to get a collection of SaleService instances, where related property.parent_id is null OR if there are some records in sale_services table sharing same parent_id through properties table, distincted by that field and order by rank.
a result should be
+----+-------------+------+
| id | property_id | rank |
+----+-------------+------+
| 1 | 2 | 5 |
| 3 | 5 | 6 |
| 4 | 6 | 7 |
+----+-------------+------+
- all items from sale_services table except (sale_service.id = 2), because it's property shares same parent_id with item (sale_service.id = 3) and item with (sale_service.id = 3) has highest rank value
I figured out SQL code to get desired result,
SELECT *
FROM
(SELECT DISTINCT ON (properties.parent_id) *
FROM "sale_services"
INNER JOIN "properties" ON "sale_services"."property_id" = "properties"."id"
WHERE ("properties"."parent_id") IS NOT NULL
ORDER BY "properties"."parent_id", "sale_services"."rank" DESC) AS sub
UNION
SELECT *
FROM "sale_services"
INNER JOIN "properties" ON "sale_services"."property_id" = "properties"."id"
WHERE ("properties"."parent_id") IS NULL
but I cant reach same with Eloquent Builder.
I tried something like this
$queryWithParent = SaleService::query()
->select(\DB::raw('DISTINCT ON (properties.parent_id) *'))
->whereNotNull('properties.parent_id')
->join('properties', 'sale_services.property_id', '=', 'properties.id')
->orderBy('parent_id')
->orderBy('sale_services.index_range', 'desc');
$queryWithoutParent = SaleService::query()
->join('properties', 'sale_services.property_id', '=', 'properties.id')
->whereNull('properties.parent_id');
$query = $queryWithParent->union($queryWithoutParent);
but got an error
SQLSTATE[42601]: Syntax error: 7 ERROR: syntax error at or near "union" LINE 1: ...perties.type <> 'hotel') order by "parent_id" asc union sele... ^ (SQL: select DISTINCT ON (properties.parent_id) * from "sale_services" inner join "properties" on "sale_services"."property_id" = "properties"."id" where ("properties"."parent_id") is not null and ("sale_services"."deleted_at") is null and "published" = 1 and exists (select 1 from "properties" where properties.id = sale_services.property_id AND properties.type <> 'hotel') order by "parent_id" asc union select * from "sale_services" inner join "properties" on "sale_services"."property_id" = "properties"."id" where ("properties"."parent_id") is null and ("sale_services"."deleted_at") is null and "published" = 1 and exists (select 1 from "properties" where properties.id = sale_services.property_id AND properties.type <> 'hotel') order by "index_range" desc limit 12 offset 0)
If I remove ordering from first query ($queryWithParent) it seems working, but with random item selected in distinct query.
Is there any other way to achieve same result, or what I'm doing wrong?
The laravel query creator for "Distinct" is distinct() as in :
$queryWithParent = SaleService::query()
->distinct('properties.parent_id')
->whereNotNull('properties.parent_id')
->join('properties', 'sale_services.property_id', '=', 'properties.id')
->orderBy('parent_id')
->orderBy('sale_services.index_range', 'desc');
Does that work ?
Finally found a solution.
Use Laravel ^5.7.28 (The UNION issue has been fixed in Laravel 5.7.28: github.com/laravel/framework/pull/27589) and you will not need a subquery!
Specify table columns to select to avoid column name collision sale_services.*
$queryWithParent = SaleService::query()
->select(\DB::raw('DISTINCT ON (properties.parent_id) sale_services.*'))
->from('sale_services')
->join('properties', 'sale_services.property_id', '=', 'properties.id')
->whereNotNull('properties.parent_id')
->orderBy('properties.parent_id')
->orderBy('sale_services.index_range', 'desc');
$queryWithoutParent = SaleService::query()
->select(\DB::raw('sale_services.*'))
->join('properties', 'sale_services.property_id', '=', 'properties.id')
->whereNull('properties.parent_id');
$query = $queryWithParent->union($queryWithoutParent);

How do I query array element in impala?

I created a table in impala ,the have two columns ,
+-----------+---------------------+---------+
| name | type | comment |
+-----------+---------------------+---------+
| unique_id | string | |
| cmap | array<struct< | |
| | fieldname:string, | |
| | fieldid:string, | |
| | fielddata:string | |
| | >> | |
+-----------+---------------------+---------+
i need set the conditions for cmap to query unique_id, such as
(fieldname="ip"and fielddata="192.168.1.145") and(fieldname="wid" and fielddata="15")
i wrote this sql but can't query it, but i inserted the data in the table
sql:
select unique_id from s_click_parquet,s_click_parquet.cmap as lst where ( fieldname="ip" and fieldData="192.168.1.145") and(fieldname="wid" and fielddata="15");
You typically access arrays with square brackets, but you can have an array of structs, not just an array.
You can INLINE() an array of struct to make it multiple rows, as well as splitting the fields to queryable columns.
Then you can query it as you would a normal table.
Play around with
select *
from table1 t
lateral view inline(cmap) a;
From my work collogue:
so rule of thumb is structs can get accessed via strucName., while arrays have to be joined*
So if we have the following types from an avro schema:
case class Visit (var Patient: PatientType = null)
case class PatientType(
var Name: String = null,
var Address: List[AddressType] = null
)
case class AddressType(
var Line1: String = null,
var Line2: String = null,
var City: String = null,
var State: String = null,
var ZipCode: String = null
)
You would query for patient name and address like:
select vis.patient.name, addr.item.*
from visit vis
left join vis.patient.address addr

Get records from a collection based on a list of integer ids

I have a list of integers: ids. There is also collection, IdNames which comes from an sql table. For each integer in ids I want to find the matching id in, IdNames. Then for each record in IdNames that has a matching id I'd like to select the value in the Name and DisplayName columns and the id.
So here is the table IdNames
Id | Name | DisplayName
--------------------------------
1 | fistName | firstDisplayName
2 | secondName | secondDisplayName
3 | thirdName | thirdDisplayName
If ids contained the integers 2 and 3, I'd want this collection to be returned
Id | Name | DisplayName
--------------------------------
2 | secondName | secondDisplayName
3 | thirdName | thirdDisplayName
How would I write this as a linq query?
I stared writing it like this: IdNames.Select(x => x.Id == ids.Any()), but obviously it's not right.
var idNames = from idName in DataContext.IdNames
where ids.Contains(idName.Id)
select idName;
Good enough?
Use Join in Linq-To-Objects("I have a list of integers: ids. There is also collection, IdNames"):
var query = from id in ids
join idName in IdNames
on id equals idName.Id
select idName;
Why is LINQ JOIN so much faster than linking with WHERE?

How to write a left join with many to many relationship

I have the following entities:
Entity: Department
DepartmentId (int)
Name (int)
SuperiorDepartmentId (int, foreign key to department entity)
DepartmentPermissions (ICollection)
Entity: DepartmentPermission
DepartmentId (int)
UserId (int)
Permission (String)
Departments (ICollection)
Entity: User
UserId (int)
Name (string)
DepartmentPermissions (ICollection)
I need to return in my query all of the departments (including the ones where the user don't have permission) and the name of the permission when the user has any.
Department
DepartmentId | Name | SuperiorDepartmentId
1 | Sales | null
2 | Internal Sales | 1
3 | Marketing | null
DepartmentPermissions
DepartmentId | User Id | Permission
1 | 2 | r
2 | 2 | rw
1 | 3 | rw
User
UserId | Name
1 | John
2 | Mary
3 | Paul
If I ask for the data for user Mary (id=2), we shoud have as result set:
DepartmentId | Name | SuperiorDepartmentId | Permission
1 | Sales | null | r
2 | Internal Sales | 1 | rw
3 | Marketing | null | null
How can I do this?
I'm going to presume the existence of a navigation property Department.DepartmentPermissions.
var query = from d in context.Departments
select new {
Department = d,
Permissions = d.DepartmentPermissions
.Where(dp => dp.UserId == 2)
.Select(p => p.Permission)
};
var result = query.AsEnumerable()
.Select(x =>
new {
x.Department.DepartmentId,
x.Department.Name,
x.Department.SuperiorDepartmentId,
Permissions =
string.Join(", ", x.Permissions.DefaultIfEmpty("null")
}
First the raw data are collected (query), then the final results are composed in memory. The latter is done because EF won't allow string.Join in a LINQ query. Your data structure allows for more than one permission per user in one department, hence the string.Join.
If you're absolutely sure that there will always be one permission you can do
Permission = d.DepartmentPermissions.Where(dp => dp.UserId == 2)
.Select(p => p.Permission)
.FirstOrDefault()
in query, and your done.

Select nested list

I have three tables in DB like this:
products
+----+------+
| id | name |
+----+------+
| 1 | prod1|
+----+------+
values
+----+---------+---------+
| id | value | alias |
+----+---------+---------+
| 1 | 10 g | 10m |
+----+---------+---------+
prdoucts_values
+---------------+---------+
| product_id | value_id|
+---------------+---------+
| 1 | 1 |
+---------------+---------+
How select all products from DB, that have any value from List<Values.alias> ?
List<decimal> aliases = ...
var query = db.Products.Where(p => p.Values.Any(v => aliases.Contains(v.Alias)));
Or (if you don't have navigation properties)
var query = from p in db.Products
join pv in db.ProductsValues on p.ProductId equals v.ProductId
join v in db.Values on pv.ValueId equals v.ValueId into values
where values.Any(v => aliases.Contains(v.Alias))
select p
If you are using EF (database first) then the table prdoucts_values is not part of the conseptual model.
instead EF givs you a direct path from Products to Values
Therefore you can write a query that look like this:
var lst = new List<Products>();
lst = db.Products.Where(c => c.Values.Any()).ToList();

Resources