Convert SQL Select Query to Magento Collection - mysql

I have a complex sql select query which actually returns sales report against specified categories. I want to load data as a collection to display but I'm not able to convert the query into Magento's equivalent getCollection statement. my query is below. thanks in advance
select
sfo.increment_id as 'orderid',
sfoi.sku,
sfoi.qty_ordered as 'qty',
IFNULL(sfoi.price_incl_tax-sfoi.discount_amount, 0) as 'productprice',
sfo.shipping_amount,
cost.value as 'price',
sfo.status,
sfo.created_at,
IF(ccp1.product_id IS NULL, 'no', 'yes') as sale,
IF(ccp2.product_id IS NULL, 'no', 'yes') as home
from sales_flat_order as sfo
join sales_flat_order_item as sfoi on sfo.entity_id = sfoi.order_id
join catalog_product_entity_decimal as cost on cost.entity_id = sfoi.product_id and cost.attribute_id = 79
left join catalog_category_product as ccp1 on sfoi.product_id = ccp1.product_id and ccp1.category_id = 8
left join catalog_category_product as ccp2 on sfoi.product_id = ccp2.product_id and ccp2.category_id = 7
where sfo.created_at between '2017-01-01' and '2017-01-02'
and sfo.status in ('processing','complete')

Magento collection statement
$collection = Mage::getModel('sales/order')->getCollection();
$collection->getSelect()->join(
array('sfoi' => $collection->getTable('sales/order_item')), 'main_table.entity_id=sfoi.order_id',
array('qty' => 'sfoi.qty_ordered', 'sku')
)->join(
array('cost' => 'catalog_product_entity_decimal'), 'cost.entity_id=sfoi.product_id and cost.attribute_id=79',
array('price' => 'cost.value')
)->joinLeft(
array('ccp1' => 'catalog_category_product'),
'sfoi.product_id = ccp1.product_id and ccp1.category_id = 8', null
)->joinLeft(
array('ccp2' => 'catalog_category_product'),
'sfoi.product_id = ccp2.product_id and ccp2.category_id = 7', null
);
$collection->addFieldToSelect('increment_id', 'orderid')
->addFieldToSelect('shipping_amount')
->addFieldToSelect('status')
->addFieldToSelect('created_at')
->addExpressionFieldToSelect(
'productprice',
'IFNULL((sfoi.price_incl_tax - {{discount_amount}}), "0")',
array(
'discount_amount' => 'sfoi.discount_amount'))
->addExpressionFieldToSelect(
'sale',
'IF({{product_id}} IS NULL, "no", "yes")',
array(
'product_id' => 'cpp1.product_id'))
->addExpressionFieldToSelect(
'home',
'IF({{product_id}} IS NULL, "no", "yes")',
array(
'product_id' => 'ccp2.product_id'))
->addAttributeToFilter('sfo.created_at', array(
'from' => '2017-01-01',
'to' => '2017-01-02',
'date' => true,
))
->addFieldToFilter('status', array('in' => array('processing','complete')));
...generated sql query
SELECT `main_table`.`increment_id` AS `orderid`,
`main_table`.`shipping_amount`,
`main_table`.`status`,
`main_table`.`created_at`,
`sfoi`.`qty_ordered` AS `qty`,
`sfoi`.`sku`,
`cost`.`value` AS `price`,
Ifnull(( sfoi.price_incl_tax - sfoi.discount_amount ), "0") AS
`productprice`,
IF(cpp1.product_id IS NULL, "no", "yes") AS `sale`,
IF(ccp2.product_id IS NULL, "no", "yes") AS `home`
FROM `sales_flat_order` AS `main_table`
INNER JOIN `sales_flat_order_item` AS `sfoi`
ON main_table.entity_id = sfoi.order_id
INNER JOIN `catalog_product_entity_decimal` AS `cost`
ON cost.entity_id = sfoi.product_id
AND cost.attribute_id = 79
LEFT JOIN `catalog_category_product` AS `ccp1`
ON sfoi.product_id = ccp1.product_id
AND ccp1.category_id = 8
LEFT JOIN `catalog_category_product` AS `ccp2`
ON sfoi.product_id = ccp2.product_id
AND ccp2.category_id = 7
WHERE ( `sfo`.`created_at` >= '2017-01-01 00:00:00'
AND `sfo`.`created_at` <= '2017-01-02 00:00:00' )
AND ( `status` IN( 'processing', 'complete' ) )

Related

Column 'available_for_order' in where clause is ambiguous

I'm strugling with this query within prestashop , i know it could be easily fixed by changing to sa.available_for_order but this way it breaks the logic of other core files ,
is there any other way around to fix this without renaming available_for_order to sa.available_for_order ,
SELECT
a.`id_product`,
b.`name` AS `name`,
`reference`,
a.`price` AS `price`,
sa.`active` AS `active`,
`newfield`,
shop.`name` AS `shopname`,
a.`id_shop_default`,
image_shop.`id_image` AS `id_image`,
cl.`name` AS `name_category`,
sa.`price`,
0 AS `price_final`,
a.`is_virtual`,
pd.`nb_downloadable`,
sav.`quantity` AS `sav_quantity`,
sa.`active`,
IF(sav.`quantity` <= 0, 1, 0) AS `badge_danger`,
sa.`available_for_order` AS `available_for_order`
FROM
`ps_product` a
LEFT JOIN
`ps_product_lang` b
ON
(
b.`id_product` = a.`id_product` AND b.`id_lang` = 1 AND b.`id_shop` = 1
)
LEFT JOIN
`ps_stock_available` sav
ON
(
sav.`id_product` = a.`id_product` AND sav.`id_product_attribute` = 0 AND sav.id_shop = 1 AND sav.id_shop_group = 0
)
JOIN
`ps_product_shop` sa
ON
(
a.`id_product` = sa.`id_product` AND sa.id_shop = a.id_shop_default
)
LEFT JOIN
`ps_category_lang` cl
ON
(
sa.`id_category_default` = cl.`id_category` AND b.`id_lang` = cl.`id_lang` AND cl.id_shop = a.id_shop_default
)
LEFT JOIN
`ps_shop` shop
ON
(
shop.id_shop = a.id_shop_default
)
LEFT JOIN
`ps_image_shop` image_shop
ON
(
image_shop.`id_product` = a.`id_product` AND image_shop.`cover` = 1 AND image_shop.id_shop = a.id_shop_default
)
LEFT JOIN
`ps_image` i
ON
(
i.`id_image` = image_shop.`id_image`
)
LEFT JOIN
`ps_product_download` pd
ON
(pd.`id_product` = a.`id_product`)
WHERE
1 AND `available_for_order` = 1
ORDER BY
a.`id_product` ASC
LIMIT 0, 50
PRESTASHOP
public function __construct()
{
parent::__construct();
$this->_select .= ',sa.`available_for_order` AS `available_for_order`, ';
$this->fields_list['sa!available_for_order'] = array(
'title' => $this->l('Available for order'),
'width' => 90,
'active' => 'available_for_order',
'filter_key' => 'sa!available_for_order',
'type' => 'bool',
'align' => 'center',
'orderby' => false
);
}
Modify your fields list to use having filter.
$this->fields_list['available_for_order'] = array(
'title' => $this->l('Available for order'),
'width' => 90,
'active' => 'available_for_order',
'filter_key' => 'available_for_order',
'havingFilter' => true,
'type' => 'bool',
'align' => 'center',
'orderby' => false
);
This will use HAVING instead of WHERE in query.
WHERE clause does not work on column aliases but HAVING does.
The column available_for_order must be in more than one of the tables. Just qualify the column name, as you do in the select:
WHERE 1 AND sa.available_for_order = 1
------------^

WHERE in (subquery) clause yii

I have a mysql query like this
(SELECT
`notification`.`id`, `notification`.`user_id` AS `user_id`, `notification`.`activity_type`, `notification`.`source_id`, `event`.`title` as sourceName,concat_ws(" ",v.firstname,v.lastname) as ActorName
FROM
`notification`
INNER JOIN
`event`
ON
event.id = notification.source_id
INNER JOIN
`user` as v
ON
v.id = notification.user_id
AND
notification.activity_type = "checkin"
where
user_id in
(SELECT friend.friend_id from friend WHERE friend.user_id=1 AND friend.is_active=1))
UNION
(SELECT
`notification`.`id`, `notification`.`user_id` AS `user_id`, `notification`.`activity_type`, `notification`.`source_id`, concat_ws(" ",u.firstname,u.lastname) as sourceName,concat_ws(" ",v.firstname,v.lastname) as ActorName
FROM
`notification`
INNER JOIN
`user` as u
ON
u.id = notification.source_id
INNER JOIN
`user` as v ON v.id = notification.user_id
AND
notification.activity_type = "friend"
where user_id in
(SELECT friend.friend_id from friend WHERE friend.user_id=1 AND friend.is_active=1) )
And i want to write that query in yii2 and i dont know how to write subquery in where in clause.
So far i have done this
$query2 ->select(['notification.id', 'notification.user_id AS user_id', 'notification.activity_type', 'notification.source_id', 'concat_ws(" ",u.firstname,u.lastname) as sourceName','concat_ws(" ",v.firstname,v.lastname) as ActorName','user_image.imagepath as image'])
->from('notification' )
->innerJoin('user as u', 'u.id = notification.source_id')
->innerJoin('user_image','user_image.user_id = notification.user_id')
->innerJoin('user as v', 'v.id = notification.user_id AND notification.activity_type = "friend"')
->where('user_image.imagetype="profile"')
->andWhere(['user_id'=>('SELECT friend.friend_id from friend WHERE friend.user_id='.$id.' AND friend.is_active=1')]);
$query ->select(['notification.id','notification.user_id AS user_id','notification.activity_type', 'notification.source_id', 'event.title as sourceName','concat_ws(" ",v.firstname,v.lastname) as ActorName','organiser.image as image'])
->from('notification')
->innerJoin('event', 'event.id = notification.source_id')
->innerJoin('organiser','organiser.organiser_id = event.organiser_id')
->innerJoin('user as v', 'v.id = notification.user_id AND notification.activity_type = "checkin"')
->Where(['in', 'user_id', [488,489]])
->union($query2);
Which generates the command query like this
(SELECT
`notification`.`id`, `notification`.`user_id` AS `user_id`, `notification`.`activity_type`, `notification`.`source_id`, `event`.`title` AS `sourceName`, concat_ws(" ",v.firstname,v.lastname) as ActorName, `organiser`.`image` AS `image`
FROM
`notification`
INNER JOIN
`event`
ON
event.id = notification.source_id
INNER JOIN
`organiser`
ON
organiser.organiser_id = event.organiser_id
INNER JOIN
`user` `v`
ON
v.id = notification.user_id
AND
notification.activity_type = "checkin"
WHERE
`user_id` IN (:qp0, :qp1))
UNION
( SELECT
`notification`.`id`, `notification`.`user_id` AS `user_id`, `notification`.`activity_type`, `notification`.`source_id`, concat_ws(" ",u.firstname,u.lastname) as sourceName, concat_ws(" ",v.firstname,v.lastname) as ActorName, `user_image`.`imagepath` AS `image`
FROM
`notification`
INNER JOIN
`user` `u`
ON
u.id = notification.source_id
INNER JOIN
`user_image`
ON
user_image.user_id = notification.user_id
INNER JOIN
`user` `v`
ON
v.id = notification.user_id
AND
notification.activity_type = "friend"
WHERE
(user_image.imagetype="profile") AND (`user_id`=:qp2) )
But its not working,so what is the right syntax
and also feel free to give suggestion if where in could be write with inner join
that would make it very easy to write query
thank you
Try This
$query = (new Query())
->select('notification`.`id`, `notification`.`user_id` AS `user_id`, `notification`.`activity_type`, `notification`.`source_id`, `event`.`title` as sourceName,concat_ws(" ",v.firstname,v.lastname) as ActorName')
->from('notification')
->innerJoin('event' ,'event.id = notification.source_id')
->innerJoin('user v' ,'v.id = notification.user_id')
->where([
'user_id' => (new Query())
->select('friend.friend_id')
->from('friend')
->where([
'friend.user_id' => 1,
'friend.is_active' => 1
])
])->andWhere([
'notification.activity_type' => "checkin"
]);
$query2 = (new Query())
->select('`notification`.`id`, `notification`.`user_id` AS `user_id`, `notification`.`activity_type`, `notification`.`source_id`, concat_ws(" ",u.firstname,u.lastname) as sourceName,concat_ws(" ",v.firstname,v.lastname) as ActorName')
->from('notification')
->innerJoin('user v' ,'v.id = notification.user_id')
->where([
'user_id' => (new Query())
->select('friend.friend_id')
->from('friend')
->where([
'friend.user_id' => 1,
'friend.is_active' => 1
])
])->andWhere([
'notification.activity_type' => "checkin"
])->union($query)->all();
You can use Yii2 Query Builder, here is an example.
$subQuery = (new \yii\db\Query())->select([
'users.id as userId',
'users.first_name',
'users.username',
'users.last_name',
'users.sector_id',
])
->from(['users'])
->where(['=', 'role_id', 3])
->orWhere(['=', 'role_id', 2])
->groupBy('users.id');
$query = (new \yii\db\Query())->select(['user.*', 'user_address.*'])
->from(['user'=>$subQuery])
->leftJoin('user_address', 'user.userId = user_address.user_id')
->where(['between', 'latitude', $min_lat, $max_lat])
->andWhere(['between', 'longitude', $min_lon, $max_lon])
->groupBy('user.userId');

How to implement ON ( ... OR ...) mysql query in doctrine2 queryBuilder

I have a SQL query in MYSQL:
For example
SELECT s.* FROM vplanning.cities as c1
INNER JOIN vplanning.cities as c2
ON (c1.id = c2.area_id)
INNER JOIN vplanning.storages as s
ON (s.city_id = c2.id OR s.city_id = c1.id)
WHERE c1.id = 109;
In doctrine I can write something like this (from my work code):
$query = $em->getRepository('VplanningPageBundle:Storage')
->createQueryBuilder('s')
->innerJoin('s.city', 'c1')
->innerJoin('c1.area', 'c2')
->innerJoin('s.storagestype', 'st')
->where('c2.id = :cityID')
->andWhere('st.typename = :storagesTypeName')
->andWhere('s.active = :active')
->setParameters(array(
'cityID' => $cityID,
'storagesTypeName' => $storagesTypeName,
'active' => 1
))
->orderBy('s.adress')
->getQuery();
As you can see, I show my relation in
->innerJoin('s.city', 'c1')
but I need also relation like
->innerJoin('s.city', 'c2')
with this condition:
ON (s.city_id = c2.id OR s.city_id = c1.id)
But it throws this error:
Error: 'c2' is already defined
c1 and c2 are the same entity and have a inner relation.
Try this:
$repository = $em->getRepository('VplanningPageBundle:Storage');
$qb = $repository->createQueryBuilder('storage');
//We can now use the expr()
$qb->join('storage.city', 'city', Join::WITH)
->join('city.area', 'area', Join::WITH, $qb->expr()->eq('area.id', ':cityID'))
->join('storage.storagestype', 'type', Join::WITH, $qb->expr()->eq('type.typename', ':storagesTypeName'))
->where('storage.active = :active')
->setParameters(array(
'cityID' => $cityID,
'storagesTypeName' => $storagesTypeName,
'active' => 1
))
->orderBy('storage.adress');
$query = $qb->getQuery();
Try smth like this
$qb = $this->getRepository('VplanningPageBundle:City')->createQueryBuilder('c');
$qb->leftJoin('c.area', 'a')
->join('c.storage', 's', Join::ON, $qb->expr()->orX($qb->expr()->eq('c.id', 's.id'), $qb->expr()->eq('a.id', 's.id')))
->innerJoin('s.storagestype', 'st')
->where('c.id = :cityID')
->andWhere('st.typename = :storagesTypeName')
->andWhere('s.active = :active')
->setParameters(array(
'cityID' => $cityID,
'storagesTypeName' => $storagesTypeName,
'active' => 1,
))
->orderBy('s.adress')
->getQuery()
;
The solution of the problem was very difficult as for me, I have to study it :)
This is a answer for my question from some forum board:
$qb = $em->getRepository('VplanningPageBundle:Storage')->createQueryBuilder('storage');
$query = $qb->join('storage.city', 'city1', Join::WITH)
->leftJoin('city1.area', 'area', Join::WITH, $qb->expr()->eq('area.id', ':cityID'))
->leftJoin('storage.city', 'city2', Join::WITH, $qb->expr()->eq('city2.id', ':cityID'))
->join('storage.storagestype', 'type', Join::WITH, $qb->expr()->eq('type.typename', ':storagesTypeName'))
->where('storage.active = :active')
->andWhere($qb->expr()->orX($qb->expr()->isNotNull('city2'), $qb->expr()->isNotNull('area')))
->setParameters(array(
'cityID' => $cityID,
'storagesTypeName' => $storagesTypeName,
'active' => 1
))
->orderBy('storage.adress')
->getQuery();

What is wrong with this SQL for phpBB?

Updated SQL: Environment is MySQL 5.5. The SQL is being generated through a phpBB abstraction layer but when I see the SQL it looks valid.
SELECT f.*, t.*, p.*, u.*, tt.mark_time AS topic_mark_time, ft.mark_time AS forum_mark_time
FROM (phpbb_posts p CROSS JOIN phpbb_users u CROSS JOIN phpbb_topics t) LEFT JOIN
phpbb_forums f ON (t.forum_id = f.forum_id) LEFT JOIN phpbb_topics_track tt ON
(t.topic_id = tt.topic_id AND tt.user_id = 2) LEFT JOIN phpbb_forums_track ft ON
(f.forum_id = ft.forum_id AND ft.user_id = 2) WHERE p.topic_id = t.topic_id AND
p.poster_id = u.user_id AND p.post_time > 1380495918 AND p.forum_id IN (7, 6, 5, 3, 4, 2, 1)
AND p.post_approved = 1 ORDER BY t.topic_last_post_time DESC, p.post_time LIMIT
18446744073709551615
Error is:
Unknown column 't.topic_id' in 'on clause' [1054]
All column names exist. All tables exist. All aliases exist.
Here's the associated code:
$sql_array = array(
'SELECT' => 'f.*, t.*, p.*, u.*, tt.mark_time AS topic_mark_time, ft.mark_time AS forum_mark_time',
'FROM' => array(
POSTS_TABLE => 'p',
USERS_TABLE => 'u',
TOPICS_TABLE => 't'),
'WHERE' => "$topics_posts_join_sql
AND p.poster_id = u.user_id
$date_limit_sql
$fetched_forums_str
$new_topics_sql
$remove_mine_sql
$filter_foes_sql
AND p.post_approved = 1",
'ORDER_BY' => $order_by_sql
);
$sql_array['LEFT_JOIN'] = array(
array(
'FROM' => array(FORUMS_TABLE => 'f'),
'ON' => 't.forum_id = f.forum_id'
),
array(
'FROM' => array(TOPICS_TRACK_TABLE => 'tt', FORUMS_TRACK_TABLE => 'ft'),
'ON' => "t.topic_id = tt.topic_id AND tt.user_id = $user_id"
),
array(
'FROM' => array(FORUMS_TRACK_TABLE => 'ft'),
'ON' => "f.forum_id = ft.forum_id AND ft.user_id = $user_id"
)
);
$sql = $db->sql_build_query('SELECT', $sql_array);
t.topic_id = TOPICS_TRACK_TABLE.topic_i
Is wrong. Make sure it uses the correct alias tt

CakePHP 3 - How do I use left joins in a query

I have the following query that works, but after talking to the client I realized I need left joins to get the desired results and I don't know how to do it.
This is my query:
$query = $this->AssetStatusAudits->find('all', [
'contain' => [
'AssetStatus',
'Users',
'Assets' => function ($q) {
return $q->contain([
'Employees',
'Sites'
]);
}
],
'conditions' => [
'AssetStatusAudits.created >' => $from,
'AssetStatusAudits.created <' => $to
]
]);
$query->select([
'createdDate' => 'date(AssetStatusAudits.created)',
'assetStatus' => 'AssetStatus.desc_en',
'firstName' => 'Users.first_name',
'lastName' => 'Users.last_name',
'count' => $query->func()
->count('*')
])
->group('date(AssetStatusAudits.created)', 'assetStatus.desc_en', 'users.last_name')
->order('Users.last_name', 'Users.created');
The above query generates this in cake:
SELECT date(AssetStatusAudits.created) AS `createdDate`,
AssetStatus.desc_en AS `assetStatus`,
Users.first_name AS `firstName`,
Users.last_name AS `lastName`,
(COUNT(*)) AS `count`
FROM asset_status_audits AssetStatusAudits
INNER JOIN asset_status AssetStatus ON AssetStatus.id = (AssetStatusAudits.asset_status_id)
INNER JOIN users Users ON Users.id = (AssetStatusAudits.user_id)
INNER JOIN assets Assets ON Assets.id = (AssetStatusAudits.asset_id)
LEFT JOIN employees Employees ON Employees.id = (Assets.employee_id)
INNER JOIN sites Sites ON Sites.id = (Assets.site_id)
WHERE (AssetStatusAudits.created > date('2016-01-13') AND AssetStatusAudits.created < date('2016-05-03'))
GROUP BY date(AssetStatusAudits.created)
ORDER BY Users.last_name
But this is the query that I need to add to cakePHP and I don't know how to do it:
SELECT
date(asset_tracking.asset_status_audits.created) as 'Date',
asset_tracking.asset_status.desc_en as 'Status',
asset_tracking.users.first_name as 'First Name' ,
asset_tracking.users.last_name as 'Last Name',
count(*)
FROM asset_tracking.asset_status_audits
LEFT OUTER JOIN asset_tracking.assets ON asset_tracking.asset_status_audits.asset_id = asset_tracking.assets.id
LEFT OUTER JOIN asset_tracking.asset_status ON asset_tracking.asset_status_audits.asset_status_id = asset_tracking.asset_status.id
LEFT OUTER JOIN asset_tracking.users ON asset_tracking.asset_status_audits.user_id = asset_tracking.users.id
LEFT OUTER JOIN asset_tracking.employees ON asset_tracking.assets.employee_id = asset_tracking.employees.id
LEFT OUTER JOIN asset_tracking.sites ON asset_tracking.assets.site_id = asset_tracking.sites.id
WHERE asset_tracking.asset_status_audits.created between date('2016-01-13') and date('2016-05-19')
group by
date(asset_tracking.asset_status_audits.created) ,
asset_tracking.asset_status.desc_en,
asset_tracking.users.last_name
ORDER BY asset_tracking.users.last_name, asset_tracking.users.created;