Entity framework awful join - entity-framework

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.

Related

I want to get product list with category and category listed as in two different columns in woocommerce

i want to get product list with parent category and subcategory as seperate in the result in woocommerce
This code I have tried
SELECT
p.ID,
p.post_title,
`post_content`,
`post_excerpt`,
t.name AS product_category,
t.term_id AS product_id,
t.slug AS product_slug,
tt.term_taxonomy_id AS tt_term_taxonomia,
tr.term_taxonomy_id AS tr_term_taxonomia,
MAX(CASE WHEN pm1.meta_key = '_price' then pm1.meta_value ELSE NULL END) as price,
MAX(CASE WHEN pm1.meta_key = '_regular_price' then pm1.meta_value ELSE NULL END) as
regular_price,
MAX(CASE WHEN pm1.meta_key = '_sale_price' then pm1.meta_value ELSE NULL END) as sale_price,
MAX(CASE WHEN pm1.meta_key = '_sku' then pm1.meta_value ELSE NULL END) as sku
FROM wp_posts p
LEFT JOIN wp_postmeta pm1 ON pm1.post_id = p.ID
LEFT JOIN wp_term_relationships AS tr ON tr.object_id = p.ID
JOIN wp_term_taxonomy AS tt ON tt.taxonomy = 'product_cat' AND tt.term_taxonomy_id =
tr.term_taxonomy_id
JOIN wp_terms AS t ON t.term_id = tt.term_id
WHERE p.post_type in('product', 'product_variation') AND p.post_status = 'publish' AND
p.post_content <> ''
GROUP BY p.ID,p.post_title
Any Idea,thanks in advance
If you want to get a list of products with parent_category and sub_category in an array format, then you can use WordPress WP_Query. Try following code snippet.
<?php
// fetch all products from woocommerce
$args = array('post_type' => 'product', 'posts_per_page' => -1);
$loop = new WP_Query($args);
$product_list_array = []; // result array with product_id,parent_category and sub_category
while ($loop->have_posts()) : $loop->the_post();
global $product;
$product_id = $product->get_id();
$result = []; $parent_cats = []; $sub_cats = [];
$result['product_id'] = $product_id;
$categories = get_the_terms($product_id, 'product_cat'); // get category list of a product
foreach ($categories as $category) {
// check if it is a parent or child
if ($category->parent == 0) {
$parent_cats[] = $category->name;
} else {
$parent_cat = get_term_by('id', $category->parent, 'product_cat'); // to get parent category name
$sub_cats[] = $category->name;
$parent_cats[] = $parent_cat->name;
}
}
// if multiple category exist then join them to a string
$result['parent_category'] = implode(" | ", array_unique($parent_cats));
$result['sub_category'] = implode(" | ", array_unique($sub_cats));
$product_list_array[] = $result;
endwhile;
?>
The $product_list_array should contain product_id,parent_category and sub_category.

MOODLE: retrieve database query

$condgrade = $DB->get_recordset_sql('SELECT c.fullname, gi.itemtype, gi.itemname,
gg.userid, gg.finalgrade FROM
'.$CFG->prefix.'grade_items gi
JOIN '.$CFG->prefix.'grade_grades gg ON gi.id =
gg.itemid JOIN '.$CFG->prefix.'course c ON c.id =
gi.courseid WHERE
gi.itemtype IN ("course") AND
gg.userid = '.$USER->id.'');
foreach($condgrade as $cgg)
{
$this->content->text .= html_writer::div($cgg->fullname.' : '.round($cgg->finalgrade).'%', array('class' => 'cours'));
}
Is there any other way of retrieving data directly from the query, rather using foreach loop everytime something like in mysql : mysql_fetch_object($users);
$records = $DB->get_records_sql('SELECT c.fullname, gi.itemtype, gi.itemname,
gg.userid, gg.finalgrade FROM
{grade_items} gi
JOIN {grade_grades} gg ON gi.id =
gg.itemid JOIN {course} c ON c.id =
gi.courseid WHERE
gi.itemtype IN ("course") AND
gg.userid = :userid', array('userid' => $USER->id));
This will return an array of records.
Note also, the use of {tablename} notation, rather than manually inserting the $CFG->prefix and the use of ':userid' bound parameter + parameter list passed as "array('userid' => $USER->id)", which helps to protect you from SQL injection attacks.
Get a single record?
$user = $DB->get_record('user', array('id' => 1));
http://docs.moodle.org/dev/Data_manipulation_API#Getting_a_single_record
EDIT : As well as the recommendations from davosmith, you also need to have a unique id in the first column. Also you can just use $DB->get_records_sql() - use $DB->get_recordset_sql() if you expect a large amount of data. If you do use get_recordset_sql() then you will also need to check the query is valid with $condgrade->valid() and close it after $condgrade->close();
$sql = "SELECT gg.id,
c.fullname,
gi.itemtype,
gi.itemname,
gg.userid,
gg.finalgrade
FROM {grade_items} gi
JOIN {grade_grades} gg ON gi.id = gg.itemid
JOIN {course} c ON c.id = gi.courseid
WHERE gi.itemtype = :itemtype
AND gg.userid = :userid";
$params = array('itemtype' => 'course', 'userid' => $USER->id);
$condgrade = $DB->get_records_sql($sql, $params);
This will return an array of record objects. The array key will be whatever is in the first column. eg:
$condgrade[1] = $recordobject;
So you can access a record object directly eg:
with $condgrade[1]->fullname
But you won't know what the id is unless you retrieve it from the database. So I'm not clear why you want to access the object directly? If you can give me an example of how you would use this directly then I can try to give an answer.

left join with subqueries in laravel

How can i do to reproduce this sql statement below with query builder or eloquent? I've tried using DB::raw... and join('something', function ($join)... but it is not working. If anyone knows how to figure this out, please show me an example.
SELECT
musica.titulo,
p.qtd_pedidos,
a.qtd_avaliacoes,
a.media_avaliacoes
FROM
musica
LEFT JOIN (
SELECT musica_id, COUNT(pedido.id) as qtd_pedidos
FROM pedido GROUP BY pedido.musica_id
) as p ON p.musica_id = musica.id
LEFT JOIN (
SELECT musica_id, COUNT(avaliacao.id) as qtd_avaliacoes,
ROUND(AVG(avaliacao.nota),1) as media_avaliacoes
FROM avaliacao GROUP BY avaliacao.musica_id
) as a ON a.musica_id = musica.id
Assuming you have described Musica, Pedido and Avaliacao models with their relations:
Musica::with(
array('pedido' => function($query) {
$query->select(DB::raw('musica_id, COUNT(pedido.id) as qtd_pedidos'))
->groupBy('musica_id');
},
'avaliacao' => function($query) {
$query->select(DB::raw(
'musica_id, '
. 'COUNT(avaliacao.id) as qtd_avaliacoes, '
. 'ROUND(AVG(avaliacao.nota),1) as media_avaliacoes'
))
->groupBy('musica_id');
}
)
)->get();

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)', '');

How do left outer joins work on Zend framework

I have this SQL query:
SELECT pais FROM pais LEFT OUTER JOIN users_has_pais ON pais.id = users_has_pais.pais_id WHERE users_has_pais.users_id = 100
And I'm trying to write something similar within a model using the leftJoin method from Zend_Db_Table but I have no clue on what I'm doing.... I tried with something like this:
$resultSetPais = Zend_Db_Table::getDefaultAdapter();
$some = $resultSetPais->select()
->joinLeft( array ( 'users_has_pais' => 'users' ),
'pais.id = users_has_pais.pais_id', 'pais' );
But truth is I have no idea how to make it work and this code just returns the adapter information.
SOLVED:
$instance = Zend_Db_Table_Abstract::getDefaultAdapter();
$pais = $instance->select();
$pais->from(array('p' => 'pais'), array('p.pais') )
->join( 'users_has_pais', 'p.id = users_has_pais.pais_id' )
->where( 'users_has_pais.users_id = ?', $row->id );
$paisEntry = $instance->fetchCol($pais);
I'm adding the answer to the question as suggested by #Jaitsu. For this kind of left join:
SELECT pais FROM pais LEFT OUTER JOIN users_has_pais ON pais.id = users_has_pais.pais_id WHERE users_has_pais.users_id = 100
The code should be something like:
$instance = Zend_Db_Table_Abstract::getDefaultAdapter();
$pais = $instance->select();
$pais->from(array('p' => 'pais'), array('p.pais') )
->join( 'users_has_pais', 'p.id = users_has_pais.pais_id' )
->where( 'users_has_pais.users_id = ?', $row->id );
$paisEntry = $instance->fetchCol($pais);
hey this code produces an INNER JOIN sql query, not an OUTER JOIN - it is different thing, right? so, what should be the correct way of doing OUTER JOIN?