Best approach to add a case in a column for nhibernate - nhibernate-criteria

What is it the best way to add this query as projection in an nhibernate?
CASE
WHEN account.firstname = '' AND account.lastname = ''
THEN email.EmailAddress
ELSE account.firstname + ' ' + account.lastname
END

Should be something like this:
var accounts = session.QueryOver<Account>()
.Select(Projections.Conditional(Restrictions.And(
Restrictions.Eq("firstname", ""),
Restrictions.Eq("lastname", "")),
Projections.Concat(p.LastName, ", ", p.FirstName),
Projections.Property(t => t.EmailAddress)))
.List();

Related

Entity framework awful join

await ctx.TBL_MST_TRANSPORT_CONFIG
.GroupJoin(ctx.TBL_MST_PROVINCE,
a=> a.from_province,
b=> b.province_code,
(a, b)=> new { a, b })
.SelectMany(x => x.b.DefaultIfEmpty(),
(aa, bb) => new { aa, bb })
.GroupJoin(ctx.TBL_MST_PROVINCE,
c=> c.aa.a.to_province,
d=> d.province_code,
(c, d)=> new { c, d })
.SelectMany(x => x.d.DefaultIfEmpty(),
(cc, dd) => new { cc, dd })
.Where(x => x.cc.c.aa.a.from_province == province &&
x.cc.c.aa.a.flag == true)
.Select(x => new TransportDTO
{
departure = x.cc.c.aa.a.departure,
destination = x.cc.c.aa.a.destination,
from_province = x.cc.c.aa.a.from_province + "-" + x.cc.d.FirstOrDefault().province_desc,
service_provider = x.cc.c.aa.a.service_provider,
to_province = x.cc.c.aa.a.to_province + "-" + x.dd.province_desc,
transport_leave_dc = x.cc.c.aa.a.transport_leave_dc,
transport_no = x.cc.c.aa.a.transport_no,
transport_type = x.cc.c.aa.a.transport_type,
})
anywhere to reduce this x.cc.c.aa.a, it look awfully bad, is there any better way to do it? I forsee it will for each join will just increase the size of the finding
it also generate awful sql
exec sp_executesql N'SELECT
1 AS [C1],
[Project5].[departure] AS [departure],
[Project5].[destination] AS [destination],
[Project5].[from_province] + N''-'' + CASE WHEN ([Project5].[C1] IS NULL) THEN N'''' ELSE [Project5].[C2] END AS [C2],
[Project5].[service_provider] AS [service_provider],
[Project5].[to_province] + N''-'' + CASE WHEN ([Project5].[province_desc] IS NULL) THEN N'''' ELSE [Project5].[province_desc] END AS [C3],
[Project5].[transport_leave_dc] AS [transport_leave_dc],
[Project5].[transport_no] AS [transport_no],
[Project5].[transport_type] AS [transport_type]
FROM ( SELECT
[Project3].[transport_type] AS [transport_type],
[Project3].[from_province] AS [from_province],
[Project3].[to_province] AS [to_province],
[Project3].[service_provider] AS [service_provider],
[Project3].[transport_no] AS [transport_no],
[Project3].[transport_leave_dc] AS [transport_leave_dc],
[Project3].[departure] AS [departure],
[Project3].[destination] AS [destination],
[Project3].[province_desc] AS [province_desc],
[Project3].[C1] AS [C1],
(SELECT TOP (1)
[Extent5].[province_desc] AS [province_desc]
FROM [dbo].[TBL_MST_PROVINCE] AS [Extent5]
WHERE [Project3].[to_province] = [Extent5].[province_code]) AS [C2]
FROM ( SELECT
[Project2].[transport_type] AS [transport_type],
[Project2].[from_province] AS [from_province],
[Project2].[to_province] AS [to_province],
[Project2].[service_provider] AS [service_provider],
[Project2].[transport_no] AS [transport_no],
[Project2].[transport_leave_dc] AS [transport_leave_dc],
[Project2].[departure] AS [departure],
[Project2].[destination] AS [destination],
[Project2].[province_desc] AS [province_desc],
[Project2].[C1] AS [C1]
FROM ( SELECT
[Filter1].[transport_type] AS [transport_type],
[Filter1].[from_province] AS [from_province],
[Filter1].[to_province] AS [to_province],
[Filter1].[service_provider] AS [service_provider],
[Filter1].[transport_no] AS [transport_no],
[Filter1].[transport_leave_dc] AS [transport_leave_dc],
[Filter1].[departure] AS [departure],
[Filter1].[destination] AS [destination],
[Extent3].[province_desc] AS [province_desc],
(SELECT TOP (1)
[Extent4].[province_desc] AS [province_desc]
FROM [dbo].[TBL_MST_PROVINCE] AS [Extent4]
WHERE [Filter1].[to_province] = [Extent4].[province_code]) AS [C1]
FROM (SELECT [Extent1].[transport_type] AS [transport_type], [Extent1].[from_province] AS [from_province], [Extent1].[to_province] AS [to_province], [Extent1].[service_provider] AS [service_provider], [Extent1].[transport_no] AS [transport_no], [Extent1].[transport_leave_dc] AS [transport_leave_dc], [Extent1].[departure] AS [departure], [Extent1].[destination] AS [destination]
FROM [dbo].[TBL_MST_TRANSPORT_CONFIG] AS [Extent1]
LEFT OUTER JOIN [dbo].[TBL_MST_PROVINCE] AS [Extent2] ON [Extent1].[from_province] = [Extent2].[province_code]
WHERE 1 = [Extent1].[flag] ) AS [Filter1]
LEFT OUTER JOIN [dbo].[TBL_MST_PROVINCE] AS [Extent3] ON [Filter1].[to_province] = [Extent3].[province_code]
WHERE [Filter1].[from_province] = #p__linq__0
) AS [Project2]
) AS [Project3]
) AS [Project5]',N'#p__linq__0 varchar(8000)',#p__linq__0='01'
am i doing correct?
ever i format it like this, it still generate very bad query in sql
await ctx.TBL_MST_TRANSPORT_CONFIG
.GroupJoin(ctx.TBL_MST_PROVINCE,
a=> a.from_province,
b=> b.province_code,
(a, b)=> new { a, b = b.DefaultIfEmpty() })
.GroupJoin(ctx.TBL_MST_PROVINCE,
c=> c.a.to_province,
d=> d.province_code,
(c, d)=> new { c, d = d.DefaultIfEmpty() })
.Where(x => x.c.a.from_province == province &&
x.c.a.flag == true)
If you are doing multiple (left) joins in lambda syntax in LINQ, it really helps if you:
Name your lambda variables meaningful names: I prefer to name anonymous types by their member names concatenated (in this case, with _ due to the repeated join). I suffix GroupJoin results with j and GroupBy results with g.
Flatten your anonymous types as you go, including only the needed members as well.
If you do this, you may even discover you have an inconsistency in your query: why do you use the left join (GroupJoin/SelectMany) to flatten the from_province and then use FirstOrDefault on the join result anyway? You don't need to do both. Below, I kept the left join syntax:
var ans = await ctx.TBL_MST_TRANSPORT_CONFIG
.GroupJoin(ctx.TBL_MST_PROVINCE,
tc => tc.from_province,
pr => pr.province_code,
(tc, frprj)=> new { tc, frprj })
.SelectMany(tc_frprj => tc_frprj.frprj.DefaultIfEmpty(),
(tc_frprj, frpr) => new { tc_frprj.tc, frpr })
.GroupJoin(ctx.TBL_MST_PROVINCE,
tc_frpr => tc_frpr.tc.to_province,
topr => topr.province_code,
(tc_frpr, toprj)=> new { tc_frpr.tc, tc_frpr.frpr, toprj })
.SelectMany(tc_frpr_toprj => tc_frpr_toprj.toprj.DefaultIfEmpty(),
(tc_frpr_toprj, topr) => new { tc_frpr_toprj.tc, tc_frpr_toprj.frpr, topr })
.Where(tc_frpr_topr => tc_frpr_topr.tc.from_province == province &&
tc_frpr_topr.tc.flag)
.Select(tc_frpr_topr => new TransportDTO {
departure = tc_frpr_topr.tc.departure,
destination = tc_frpr_topr.tc.destination,
from_province = tc_frpr_topr.tc.from_province + "-" + tc_frpr_topr.frpr.province_desc,
service_provider = tc_frpr_topr.tc.service_provider,
to_province = tc_frpr_topr.tc.to_province + "-" + tc_frpr_topr.topr.province_desc,
transport_leave_dc = tc_frpr_topr.tc.transport_leave_dc,
transport_no = tc_frpr_topr.tc.transport_no,
transport_type = tc_frpr_topr.tc.transport_type,
});
NOTE: Since you only use frpr.province_desc and trpr.province_desc in the final result, you could change the SelectMany to just forward those fields (e.g. frprd = frpr.provice_desc) and change the lambda names (e.g. tc_frprd) and not the entire frpr object. It should make no difference to the final translated SQL query.
PS Don't compare booleans against true or false.
That's why LINQ Query syntax is better when you have a lot of joins.
var query =
from tc in ctx.TBL_MST_TRANSPORT_CONFIG
join fp in ctx.TBL_MST_PROVINCE on tc.from_province equals fp.province_code into fpj
from fp in fpj.DefaultIfEmpty()
join tp in ctx.TBL_MST_PROVINCE on tc.to_province equals tp.province_code into tpj
from tp in tpj.DefaultIfEmpty()
where tc.from_province == province && tc.flag == true
select new TransportDTO
{
departure = tc.departure,
destination = tc.destination,
from_province = tc.from_province + "-" + fp.province_desc,
service_provider = tc.service_provider,
to_province = tc.to_province + "-" + tp.province_desc,
transport_leave_dc = tc.transport_leave_dc,
transport_no = tc.transport_no,
transport_type = tc.transport_type,
}
It does not guarantee better SQL, but maintenance and readability improved a lot.

sequelize.query returns empty array

Using Sequelize 4.38.1 against PostgreSQL 10.5, I have the following code:
const criteria = {
attended: '2018-09-21',
location: 'Squirrel Hill',
teacher: 'Doe, John',
classTitle: 'Yoga 1'
};
const sql = `
SELECT
"Attendances"._id,
"Attendances"."UserId",
"Users"."lastName" || ', ' || "Users"."firstName" AS name
FROM
"Attendances" INNER JOIN "Users" ON "Attendances"."UserId" = "Users"._id
WHERE
"Attendances".attended = :attended AND
"Attendances".location = :location AND
"Attendances".teacher = :teacher AND
"Attendances"."classTitle" = :classTitle
ORDER BY "Users"."lastName", "Users"."firstName";`;
sequelize.query(
sql,
{
replacements: criteria,
type: sequelize.QueryTypes.SELECT
})
.then(attendeeList => {
console.log(attendeeList);
return res.status(200).json(attendeeList);
})
With sequelize logging turned on, my query is correctly parameterized with the replacements and executed:
SELECT
"Attendances"._id,
"Attendances"."UserId",
"Users"."lastName" || ', ' || "Users"."firstName" AS name
FROM
"Attendances" INNER JOIN "Users" ON "Attendances"."UserId" = "Users"._id
WHERE
"Attendances".attended = '2018-09-21' AND
"Attendances".location = 'Squirrel Hill' AND
"Attendances".teacher = 'Doe, John' AND
"Attendances"."classTitle" = 'Yoga 1'
ORDER BY "Users"."lastName", "Users"."firstName";
The problem: my results (attendeeList) are getting returned as []. When I run the same query using pgAdmin, I get one row:
_id UserId name
40 24601 "Doe, John"
Anyone have any thoughts as to what I'm doing wrong?
I submitted the example above as an Issue on Sequelize's Github repo as I was able to make it work with a query that did not include an INNER JOIN.
For my situation, pg-native is a better fit (considering ripping out sequelize elsewhere as it's slower)...
const client = new PGNativeClient();
client.connect(config.sequelize.uri, err => {
if(err) {
res.status(401).json({ message: 'Not able to connect to database to get list of attendees.' });
return;
}
const sql = `
SELECT
"Attendances"._id,
"Attendances"."UserId",
"Users"."lastName" || ', ' || "Users"."firstName" AS name
FROM
"Attendances" INNER JOIN "Users" ON "Attendances"."UserId" = "Users"._id
WHERE
"Attendances".attended = $1::DATE AND
"Attendances".location = $2 AND
"Attendances".teacher = $3 AND
"Attendances"."classTitle" = $4
ORDER BY "Users"."lastName", "Users"."firstName";`;
const { attended, location, teacher, classTitle } = req.query;
client.query(sql, [attended, location, teacher, classTitle], (err, rows) => {
if(err) {
res.status(424).json({ message: 'Not able to retrieve attendees from database (but connected successfully).' });
return;
}
res.status(200).json(rows);
});

trying to use & and operation in scala

case class takes 3 parameters id, applied by and internal name. Trying to make the result lenght return 3. internal name was a newly added field/parameter so that is why it is returning 0 instead of 3.
I was think to use
result.topics.find(_.topicId == "urn:emmet:1234567").get.appliedBy should be ("human") &
result.topics.find(_.topicId == "urn:emmet:2345678").get.internalName should be ("")
it's giving me syntax error, please advice thanks in advance
it should "dedup topics by id, keeping those applied by human if possible" in {
val doc = Document.empty.copy(
topics = Array(
Topic("urn:emmet:1234567", appliedBy = "machine" , internalName = ""),
Topic("urn:emmet:2345678", appliedBy = "human", internalName = ""),
Topic("urn:emmet:1234567", appliedBy = "human", internalName = ""),
Topic("urn:emmet:2345678", appliedBy = "machine", internalName = ""),
Topic("urn:emmet:3456789", appliedBy = "machine", internalName = ""),
Topic("urn:emmet:3456789", appliedBy = "machine", internalName = "")
)
)
val result = DocumentTransform.dedupSubRecords(doc)
result.topics.length should be (3)
result.topics.find(_.topicId == "urn:emmet:1234567").get.appliedBy should be ("human")
result.topics.find(_.topicId == "urn:emmet:2345678").get.appliedBy should be ("human")
result.topics.find(_.topicId == "urn:emmet:3456789").get.appliedBy should be ("machine")
}
Multiple test statements are already an 'and', because if any one of them fails the whole test fails.
val e1234567 = result.topics.find(_.topicId == "urn:emmet:1234567").get
e1234567.appliedBy shouldEqual "human"
e1234567.internalName shouldEqual ""

How do I do this sql query in Zend?

How do I do this sql query in Zend Framework, I need to some how do this in the PDO context I think? I tried ->query but not sure if I am getting this right. The three variables are user_id and to and from date.
SELECT
ss.subcategory_id,
ss.subcategory_name,
ss.subcategory_issaving,
IFNULL(SUM(m.mv_monthly_total),0) AS expendsum
FROM
(SELECT
s.subcategory_id,
s.subcategory_name,
s.subcategory_issaving
FROM
subcategory s
WHERE
s.subcategory_isexpend = 'Y'
AND
s.subcategory_issaving = 'Y') ss
LEFT JOIN
mv_monthly m
ON ss.subcategory_id = m.mv_monthly_subcategory_id
AND m.mv_monthly_user_id = 2
AND m.mv_monthly_month >= '2010-01-01'
AND m.mv_monthly_month <= '2020-01-01'
GROUP BY
ss.subcategory_id,
ss.subcategory_name,
ss.subcategory_issaving
ORDER BY
ss.subcategory_issaving DESC,
expendsum;
I have tried the following with no luck
$db = Zend_Db_Table::getDefaultAdapter();
$dbExpr1 = new Zend_Db_Expr("s.subcategory_id, s.subcategory_name, s.subcategory_issaving");
$dbExpr2 = new Zend_Db_Expr("ss.subcategory_id, ss.subcategory_name, ss.subcategory_issaving, IFNULL(SUM(m.mv_monthly_total),0) AS expendsum");
$select = $db->select()
->from(
array(
'ss' => new Zend_Db_Expr(
'('. $db->select()
->from(array("s" => "subcategory"), $dbExpr1)
->where("s.subcategory_isexpend = 'Y'")
->where("s.subcategory_issaving = 'Y'") .')'
)
),
$dbExpr2
)
->joinLeft(array("m" => "mv_monthly"), "ss.subcategory_id = m.mv_monthly_subcategory_id")
->where("m.mv_monthly_user_id = ?", $user_id)
->where("m.mv_monthly_month >= ?", $fromMonth)
->where("m.mv_monthly_month <= ?", $toMonth)
->group(array("ss.subcategory_id","ss.subcategory_name","ss.subcategory_issaving"))
->order(array("ss.subcategory_issaving DESC", "expendsum"));
$row = $db->fetchAll($select);
For such a complex query, you can just execute it directly rather than using the object oriented approach as it gets fairly complicated with a query like that.
Try something like this, replacing my query with yours, and binding your variables into the query:
$db = Zend_Db_Table::getDefaultAdapter();
$stmt = new Zend_Db_Statement_Pdo($db, 'SELECT a, b, c FROM a WHERE username = ? AND date = ?');
try {
$res = $stmt->execute(array($user_id, $fromMonth));
if ($res) {
$rows = $stmt->fetchAll();
}
} catch (Zend_Db_Statement_Exception $dbex) {
// log Query failed with exception $dbex->getMessage();
}
If you prefer to use the object oriented approach, or need to because some parts of the query will be conditional, I usually build by subqueries up first as their own select, and you can simply embed those in to the main query with the select object for the subquery.
Here is what I mean by that:
$subselect = $this->getDbTable()
->select()
->from('mytable', array('time' => 'max(time)', 'id'))
->where('id IN (?)', $serialNumbers)
->group('id');
$select = $this->getDbTable()
->select()
->setIntegrityCheck(false)
->from('mytable')
->join('other', 'mytable.id = other.id', array('label'))
->join(array('dt' => $subselect),
'(mytable.time, mytable.id) = (dt.time, dt.id)', '');

Zend Framework Complex Where Statement

This method is published as offical example
->where("price < $minimumPrice OR price > $maximumPrice")
is such method safe?
want to write it as
->where("price < ? OR price > ?", $minimumPrice, $maximumPrice)
are there any poissibility?
and I can't split it into 2 where statements because plan to write query
->where("1 OR 2")
->where("3 OR 4")
Try this:
$query->where('(price < ?', $minPrice)
->orWhere('price > ?)', $maxPrice)
->where('some = ?', $some_other_variable);
will result:
where ((price < $minPrice) OR (price > $maxPrice)) AND (some = $some_other_variable)
Note the double (( )) in OR part
If I have complex WHERE clauses I use the db adapters' ->quoteInto() method like:
$where = '('
. $dbAdapter->quoteInto('price1 < ?', $price1)
. ' OR '
. $dbAdapter->quoteInto('price1 > ?', $price1)
. ')'
. ' AND '
. '('
. $dbAdapter->quoteInto('price2 < ?', $price2)
. ' OR '
. $dbAdapter->quoteInto('price2 > ?', $price2)
. ')'
;
$select->where($where);
Some times you will want to make SQL queries which have parenthesis around multiple where conditions that would be easily parsed with foreach, but you do not want to be bothered about string manipulation. For example, you would have a list of users with id's and that have to be of certain type, you can try this:
$select = $this->select();
$subWhere = $this->select();
foreach(array_keys($idArr) as $key => $value) {
$subWhere->orWhere('id=?', $value);
}
$select->where(implode(' ', $subWhere->getPart('WHERE')))->where('type=?', 'customer');
This will result in "SELECT * FROM table WHERE ((id=X) OR (id=Y) OR (id=Z)...) AND (type='customer');"
The idea developed a bit further, you could extend the Zend_Db_Table_Abstract:
public function subWhere($col, $binds, $operands, $andOr = 'OR' )
{
$subWhere = $this->select();
if(strtolower($andOr) == 'or') {
foreach($binds as $key => $value) {
$subWhere->orWhere($col.$operands[$key].'?', $value);
}
return implode(' ', $subWhere->getPart('WHERE'));
}
elseif (strtolower($andOr) == 'and') {
foreach ($binds as $key => $value) {
$subWhere->where($col.$operands[$key].'?', $value);
}
return implode(' ', $subWhere->getPart('WHERE'));
}
else {
return false;
}
}
And use it as:
$this->select()->where($this->subWhere($col, $binds, $operands));
Of course you should allow mixed $cols, $operands = array() defaulting as '=?' etc. but for the sake of simplicity I left that out. But I believe we should use native SQL functions like IN(), BETWEEN ... AND ..., NOT BETWEEN ... AND ...? Zend Framework doesn't make your life very easy though.
$select->where($db->quoteInto('field1 < ? OR', $minPrice)
. $db->quoteInto('field1 > ?', $maxPrice))
->where($db->quoteInto('field2 < ? OR', $value2)
. $db->quoteInto('field2 > ?', $value3));