How write hibernate criteria for specific SQL? - nhibernate

i need write similar SQL by ICriteria:
Tables:
1Lvl -- 2Lvl -- 3Lvl
SQL:
SELECT * FROM 2Lvl
WHERE 2Lvl.1LvlFK in
(
SELECT 1Lvl.Id
FROM 3Lvl
JOIN 2Lvl ON 3Lvl.2LvlFK = 2Lvl.Id
JOIN 1Lvl ON 2Lvl.1LvlFK = 1Lvl.Id
WHERE 3Lvl.Id = 123
)
I'm sorry for so specific question, but I inherited project with Hibernate from exemployee and I can't still understand hibernate-criteria.

var subQuery = DetachedCriteria.For<Lvl3>("lvl3")
.CreateAlias("Lvl2", "sublvl2", JoinType.InnerJoin)
.CreateAlias("Lvl1", "lvl1", JoinType.InnerJoin)
.Add(Restrictions.EqProperty("sublvl2.Id", "lvl2.Id")
.Add(Restrictions.Eq("lvl3.Id", 123)
.SetProjection(Projections.Property("lvl1.Id"));
Session.CreateCriteria<Lvl2>("lvl2")
.Add(Subqueries.PropertyIn("Lvl1.Id", subQuery));
Should do the trick. I've made assumptions about what your entities are called since 1Lvl, etc.. are not valid C# identifiers. I've also assumed the primary key column of each table was Id. Also a note that this won't produce the exact SQL you're looking for, but it will get you the same result.

Related

Arel UpdateManager with Join creates invalid SQL - how to rephrase?

Apparently there is an issue in Arel core, where Arel::UpdateManager, when performing a column update on a join, does not generate the table name for the update column. It results in invalid SQL.
I ran into this in a Rails 5.2 app, where I had an SQL literal UPDATE statement that I was trying to rephrase in Arel.
UPDATE observations o, names n
SET o.lifeform = n.lifeform
WHERE o.name_id = n.id
AND o.lifeform != n.lifeform
In Arel, i wrote this:
names = Name.arel_table
obs = Observation.arel_table
join_source = Arel::Nodes::JoinSource.new(
obs, [obs.create_join(names)]
)
Arel::UpdateManager.new.
table(join_source).
where(obs[:id].eq(names[:id]).
and(obs[:lifeform].not_eq(names[:lifeform]))).
set([[obs[:lifeform], names[:lifeform]]])
This returns:
Mysql2::Error: Column 'lifeform' in field list is ambiguous:
The problem is at the end. The SQL generated from this does not specify the table where the column is to be set.
UPDATE `observations`
INNER JOIN `names`
SET `lifeform` = `names`.`lifeform`
WHERE (`observations`.`id` = `names`.`id`)
AND (`observations`.`lifeform` != `names`.`lifeform`)
Elsewhere, Arel-generated SQL usually qualifies columns with table names to avoid ambiguity. But the source code for update_manager.rb definitely uses Nodes::UnqualifiedColumn.new(column). (I have added my description to the Arel issue on GitHub.)
For now I'd maybe like to rephrase my Arel some other way. Is there a way to force Arel to quote the table name, similar to connection.quote_table_name?
Or would using a CTE be appropriate?
I guess one way to do this is with ActiveRecord's connection.update_all.
names = Arel::Table.new(:names)
Observation.joins(:name).
 where(names[:correct_spelling_id].not_eq(nil)).
  update_all("`observations`.`name_id` = `names`.`correct_spelling_id`")
This generates the desired SQL:
UPDATE `observations`
INNER JOIN `names`
ON (`observations`.`name_id` = `names`.`correct_spelling_id`)
AND (`names`.`correct_spelling_id` IS NOT NULL)
SET `observations`.`name_id` = `names`.`correct_spelling_id`
I think this is the way to go.

SQL "not in" syntax and nested SELECT in Entity Framework

I recently used Entity Framework. For elementary CRUD operations, I have no problems, but for more complicated queries, I do not know how to do these.
For example: how to write a nested select? How to use the NOT IN operator?
The query that I want to write is:
SELECT *
FROM course
WHERE idCourse NOT IN (SELECT idCourse
FROM reservation
WHERE idStudent = 'value');
I do not know where to start. Could you please give me some advice?
If I do not misunderstood your question, you want to know how to write the question's query as LINQ-to-Entity query.
Examples could be:
var courses = context.Courses
.Where(c => c.Reservations.All(r => r.idStudent != "value"))
.Select(c => c);
// with eager loading
var courses = (from c in context.Courses.Include(c => c.Reservations)
where c.Reservations.All(r => r.idStudent != "value")
select c).ToArray();
var courses = (from c in context.Courses
join r in context.Reservations on c.idCourse equals r.idCourse
where r => r.idStudent != "value"
select c).ToArray();
The Contains() is equivalent to EXIST IN in a query. Well, 1st and 2nd nearly the same. Only difference is the Include method to show how you could eager load data.
In the 3rd query I use the join key word to do a join operation - equivalent to an INNER JOIN. The result will contain only records where a relation between a course and a reservation exists and the searched student ID is referenced in the reservation.
If you should not use LINQ for your querys, you should take a look. It's a perfect way to decouple your data access layer from persistence layer and you could test all your queries in code too.
Here you could get a very good entry into the subject.
EDIT:
Modified example code to fit NOT IN.
If you are having trouble executing complex queries with LINQ, you can also execute raw SQL queries. They are easy and faster than linq queries.
Here is an example in c#
var list = dc.Database.SqlQuery<YourCourceClass>(#"SELECT *
FROM course
WHERE idCourse NOT IN(SELECT idCourse
FROM reservation
WHERE idStudent = 'value');").ToList();

Alternate solution for the query - Used INTERSECT function in oracle plsql

I am working on the query. I have two tables one is detail table where not grouping happen and its like including all the values and other table is line table which has important column grouped together from detail table.
I want to show all the column from line table and some column from detail table.
I am using below query to fetch my records
SELECT ab.*,
cd.phone_number,
cd.id
FROM xxx_line ab,
xxx_detail cd
WHERE cd.reference_number = ab.reference_number
AND cd.org_id = ab.org_id
AND cd.request_id = ab.request_id
AND ab.request_id = 13414224
INTERSECT
SELECT ab.*,
cd.phone_number,
cd.id
FROM xxx_line ab,
xxx_detail cd
WHERE cd.reference_number = ab.reference_number
AND cd.org_id = ab.org_id
AND cd.request_id = ab.request_id
AND ab.request_id = 13414224
The query is working fine...
But I want to know is there any other way for I can achieve the same result by not even using Intersect.
I purpose is to find out all possible way to get the same output.
The INTERSECT operator returns the unique set of rows returned by each query. The code can be re-written with a DISTINCT operator to make the meaning clearer:
SELECT DISTINCT
xxx_line.*,
xxx_detail.phone_number,
xxx_detail.id
FROM xxx_line
JOIN xxx_detail
ON xxx_line.reference_number = xxx_detail.reference_number
AND xxx_line.org_id = xxx_detail.org_id
AND xxx_line.request_id = xxx_detail.request_id
WHERE xxx_line.request_id = 13414224
I also replaced the old-fashioned join syntax with the newer ANSI join syntax (which makes relationships clearer by forcing the join tables and conditions to be listed close to each other) and removed the meaningless table aliases (because code complexity is more directly related to the number of variables than the number of characters).

Difference between DELETE and DELETE FROM in SQL?

Is there one? I am researching some stored procedures, and in one place I found the following line:
DELETE BI_Appointments
WHERE VisitType != (
SELECT TOP 1 CheckupType
FROM BI_Settings
WHERE DoctorName = #DoctorName)
Would that do the same thing as:
DELETE FROM BI_Appointments
WHERE VisitType != (
SELECT TOP 1 CheckupType
FROM BI_Settings
WHERE DoctorName = #DoctorName)
Or is it a syntax error, or something entirely different?
Assuming this is T-SQL or MS SQL Server, there is no difference and the statements are identical. The first FROM keyword is syntactically optional in a DELETE statement.
http://technet.microsoft.com/en-us/library/ms189835.aspx
The keyword is optional for two reasons.
First, the standard requires the FROM keyword in the clause, so it would have to be there for standards compliance.
Second, although the keyword is redundant, that's probably not why it's optional. I believe that it's because SQL Server allows you to specify a JOIN in the DELETE statement, and making the first FROM mandatory makes it awkward.
For example, here's a normal delete:
DELETE FROM Employee WHERE ID = #value
And that can be shortened to:
DELETE Employee WHERE ID = #value
And SQL Server allows you to delete based on another table with a JOIN:
DELETE Employee
FROM Employee
JOIN Site
ON Employee.SiteID = Site.ID
WHERE Site.Status = 'Closed'
If the first FROM keyword were not optional, the query above would need to look like this:
DELETE FROM Employee
FROM Employee
JOIN Site
ON Employee.SiteID = Site.ID
WHERE Site.Status = 'Closed'
This above query is perfectly valid and does execute, but it's a very awkward query to read. It's hard to tell that it's a single query. It looks like two got mashed together because of the "duplicate" FROM clauses.
Side note: Your example subqueries are potentially non-deterministic since there is no ORDER BY clause.
Hi friends there is no difference between delete and delete from in oracle database it is optional, but this is standard to write code like this
DELETE FROM table [ WHERE condition ]
this is sql-92 standard. always develop your code in the standard way.

"ambiguous column name" in SQLite INNER JOIN

I have two tables in a SQLite DB, INVITEM and SHOPITEM. Their shared attribute is ItemId and I want to perform an INNER JOIN. Here's the query:
SELECT INVITEM.CharId AS CharId,
INVITEM.ItemId AS ItemId
FROM (INVITEM as INVITEM
INNER JOIN SHOPITEM AS SHOPITEM
ON SHOPITEM.ItemId = INVITEM.ItemId)
WHERE ItemId = 3;
SQLite doesn't like it :
SQL error: ambiguous column name: ItemId
The error goes away if I write WHERE INVITEM.ItemId = 3, but since the WHERE condition is more or less user-specified, I rather make it work without having to specify the table. NATURAL JOIN seems to solve the issue, but I'm not sure if the solution is general enough (ie I could use in this case, but I'm not sure if I can use in every case)
Any alternate SQL syntax that would fix the problem?
I would write this query this way:
SELECT i.CharId AS CharId, i.ItemId AS ItemId
FROM INVITEM as i INNER JOIN SHOPITEM AS s USING (ItemId)
WHERE i.ItemId = 3;
I'm using the USING (ItemId) syntax which is just a matter of taste. It's equivalent to ON (i.ItemID = s.ItemID).
But I resolved the ambiguity by qualifying i.ItemID in the WHERE clause. You would think this is unnecessary, since i.ItemID = s.ItemID. They're both equal by commutativity, so there's no semantic ambiguity. But apparently SQLite isn't smart enough to know that.
I don't like to use NATURAL JOIN. It's equivalent to an equi-join of every column that exists in both tables with the same name. I don't like to use this because I don't want it to compare columns that I don't want it to, simply because they have the same name.
I would steer clear of allowing the user to write SQL clauses directly. This is the source of SQL Injection vulnerabilities.
If you need the query to be flexible, try parsing the user's input and adding the appropriate where clause.
Here is some C# code to show the general idea
// from user input
string user_column = "ItemID";
string user_value = "3";
string sql = "SELECT INVITEM.CharId AS CharId, INVITEM.ItemId AS ItemId FROM (INVITEM as INVITEM INNER JOIN SHOPITEM AS SHOPITEM ON SHOPITEM.ItemId = INVITEM.ItemId) ";
if (user_column == "ItemID")
{
// using Int32.Parse here to prevent rubbish like "0 OR 1=1; --" being entered.
sql += string.Format("WHERE INVITEM.ItemID={0}",Int32.Parse(user_value));
}
Obviously if you're dealing with more than one clause, you'd have to substitute AND for WHERE in subsequent clauses.
Just change your column alias to something similar, but unique (such as ITEM_ID).