combine two queries as one without looping mysql - mysql

how to join this in single query any help to combine these two queries as one without looping,
$today_date = mktime(0, 0, 0, $mon, $day-1, $year);
SELECT * FROM (`lead_follow_up`) LEFT JOIN `leads` ON `leads`.`id` = `lead_follow_up`.`lead_id` WHERE `date` <= $today_date GROUP BY `lead_follow_up`.`lead_id` ORDER BY `lead_follow_up`.`date` DESC
from the above query i get array $previou
$previou= Array
(
[0] => stdClass Object
(
[id] => 1
[lead_id] => 75943
[date] => 1438930800
[updated_on] => 1438884890
)
[1] => stdClass Object
(
[id] => 2
[lead_id] => 75943
[date] => 1416459600
[updated_on] => 1415901523
),
[2] => stdClass Object
(
[id] => 3
[lead_id] => 75943
[date] => 1416459600
[updated_on] => 1415901523
),....etc
);
foreach($previou as $key => $p):
$q = "SELECT `id` FROM (`lead_follow_up`) WHERE `lead_id` = '".$p->id."' AND `date` > '".$p->date."' ORDER BY `updated_on` DESC ";
if(!$this->db->query($q)){
$previouData[$key] = $p;
$pCount++;
}
endforeach;
how to join this in single query any help to combine these two queries as one without looping,

Your queries don't make much sense. For a start your first query has a GROUP BY lead_follow_up.lead_id but no aggregate functions. So in MySQL that will return one row for each value of lead_id (which row it returns is not defined).
Yet your array of sample data has multiple rows per lead_id so cannot have come from the query.
You are also LEFT OUTER JOINing the leads table, yet it doesn't seem to make sense to have a lead_follow_up which doesn't relate to a lead. As such you may as well use an INNER JOIN.
I am going to assume that what you want is a list of leads / lead_follow_ups and for each one a couple of all the follow ups after that particular follow up. That would give you something like this (making loads of assumptions as I do not know your table structure):-
SELECT leads.id AS lead_id,
lead_follow_up.id
lead_follow_up.`date`,
lead_follow_up.updated_on,
COUNT(lead_follow_up_future.id) AS future_lead_count
FROM leads
INNER JOIN lead_follow_up ON leads.id = lead_follow_up.lead_id
LEFT OUTER JOIN lead_follow_up AS lead_follow_up_future ON leads.id = lead_follow_up.lead_id AND lead_follow_up_future.`date` > lead_follow_up.`date`
WHERE lead_follow_up.`date` <= $today_date
GROUP BY leads.id AS lead_id,
lead_follow_up.id
lead_follow_up.`date`,
lead_follow_up.updated_on
ORDER BY lead_follow_up.date DESC

Related

cake php how do a good query

I have a problem, i am noob with cakePHP and i use cakePHP 1.3 and i have a query with a lot subquerys and try to convert this to queryBuilder of cake using find sentences or things like this:
My query is the next-one:
SELECT
`PbFeedback`.`id`,
`PbFeedback`.`createdby`,
`PbFeedback`.`created`,
`PbFeedback`.`msg`,
`Usuario`.`name`,
`PbFeedback`.`tipo`,
COUNT(IF(PbFeedback.msg = 0, 1, 0))AS totalMsg,
`Entidad`.`nombre`
,UltimoMensaje.* , #Subconsulta
MensajeNuevo.* #Subconsulta
FROM
# A A
`eon_feedback` AS `PbFeedback`
LEFT JOIN
# B B
`eon_sys_usuarios` AS `Usuario` ON(`PbFeedback`.`createdby` = `Usuario`.`id`)
LEFT JOIN `eon_entidades` AS `Entidad` ON(
`PbFeedback`.`entidad_id` = `Entidad`.`id`
)
LEFT JOIN (SELECT
`F`.`createdby`,
`F`.`created`,
`F`.`msg`,
`F`.`tipo`
FROM eon_feedback AS `F`
GROUP BY `F`.`createdby`
ORDER BY F.created DESC
) AS UltimoMensaje ON (UltimoMensaje.createdby = `PbFeedback`.`createdby`)
LEFT JOIN (
SELECT
F.createdby AS usuario_id,
COUNT(*) AS `count`
FROM
`eon_feedback` AS `F`
WHERE `F`.`status` = 0
GROUP BY F.createdby
) AS MensajeNuevo ON MensajeNuevo.usuario_id = PbFeedback.createdby
WHERE
`PbFeedback`.`usuario_id` = 0
GROUP BY
`PbFeedback`.`createdby`
ORDER BY
`PbFeedback`.`createdby` ASC
LIMIT 0,
30 ;
Thanks ;)
I would suggest you look into the ContainableBehavior:
http://book.cakephp.org/1.3/en/The-Manual/Core-Behaviors/Containable.html
Containable allows you to easily build complex queries, for example:
$this->PbFeedback->find('all', array(
'contain' => array (
'Usuario',
'UltimoMensaje ',
'eon_feedback' => array (
'fields' => array ('created','msg','tipo'),
'conditions' => array ('eon_feedback.status =' => '0')
)
),
'limit' => 30
);

Mysql query with multiple left joins from tables that are subsets of the "from" table not working as expected

Making a database for inbound calls with data for each type of call. I've defined a database with a "records" table, a "sales" table, and an "accounts_receivable" table. The "sales" and "accounts_receivable" tables are essentially subsets of the "records" table. Meaning, there is a corresponding row in the "records" table for each row of both the "sales" and "accounts_receivable" tables (exclusively).
My "records" table has values:
id (BIGINT)
timestamp (TIMESTAMP)
rep_id (INT)
notes (VARCHAR)
My "sales" table has values:
local_record_id (BIGINT)
record_id (BIGINT)
amount (VARCHAR)
bottles_sold (VARCHAR)
record_type (VARCHAR) default 'sales'
My "accounts_receivable" table has values:
local_record_id (BIGINT)
record_id (BIGINT)
amount (VARCHAR)
bottles_sold (VARCHAR)
record_type (VARCHAR) default 'ar'
I'm trying to pull all of the records in the entire database with the applicable data for each record. To do this, I thought a LEFT JOIN starting with the "records" table would work just fine, but for some reason it's not. This is my query:
SELECT *
FROM $table_name_records
LEFT JOIN $table_name_sales ON ($table_name_records.id = $table_name_sales.record_id)
LEFT JOIN $table_name_ar ON ($table_name_records.id = $table_name_ar.record_id)
This is returning a set of results where the data from the subset tables with similar column names are getting blanked out.
Example of output (which corresponds to an row/entry in the "sales" table of my database):
Array (
[id] => 1
[timestamp] => 2014-12-17 13:11:07
[rep_id] => 37
[notes] => Some notes for you.
[local_record_id] =>
[record_id] =>
[amount] =>
[bottles_sold] =>
[record_type] =>
)
I've verified that there is data in each of the columns of each of the subset tables of my database, so I don't understand why the values are coming back as blank. If I query with only a single left join:
SELECT *
FROM $table_name_records
LEFT JOIN $table_name_sales ON ($table_name_records.id = $table_name_sales.record_id)
I get what I expect to see:
Array (
[id] => 1
[timestamp] => 2014-12-17 13:11:07
[rep_id] => 37
[notes] => Some notes for you.
[local_record_id] => 14
[record_id] => 1
[amount] => 45.45
[bottles_sold] => Multiple
[record_type] => sales
)
Works the same way as I expect if I left join only the "accounts-receivable" table:
SELECT *
FROM $table_name_records
LEFT JOIN $table_name_ar ON ($table_name_records.id = $table_name_ar.record_id)
Array (
[id] => 1
[timestamp] => 2014-12-17 13:12:16
[rep_id] => 37
[notes] => Some notes.
[local_record_id] => 6
[record_id] => 2
[amount] => 50.89
[bottles_sold] => Single
[record_type] => ar
)
I've reviewed the awesome graphical SQL join doc MySQL Joins, and the LEFT JOIN there looks like exactly what I want: every thing from Table A (records) with the data from Table B (sales) and then also from a second Table B (accounts_receivable) attached to the appropriate rows (per the ON statement).
What am I doing wrong that this isn't responding as I'm expecting it to?
My queries where I only want "sales" records are typically structured in the other direction. Thus:
SELECT *
FROM $table_name_sales
LEFT JOIN $table_name_records ON ($table_name_records.id = $table_name_sales.record_id)
So, I tried structuring my query from that direction and then just joining them via:
SELECT *
FROM $table_name_sales
LEFT JOIN $table_name_records ON ($table_name_records.id = $table_name_sales.record_id)
UNION
SELECT *
FROM $table_name_ar
LEFT JOIN $table_name_records ON ($table_name_records.id = $table_name_ar.record_id)
This query returns exactly what I'd expect:
Array (
[id] => 1
[timestamp] => 2014-12-17 13:11:07
[rep_id] => 37
[notes] => Some notes for you.
[local_record_id] => 14
[record_id] => 1
[amount] => 45.45
[bottles_sold] => Multiple
[record_type] => sales
)
But it seems to me that structuring a query in this way might get expensive in the future when I have lots of records and more tables (corresponding to different types of inbound calls -- eg: information request, problem with order, etc). I don't have a ton of experience with MySQL though.
It looks like your sales and accounts_receivable tables have parallel structures, and that rows in each of those tables are related to your records table but not to one another.
#MichaelBerkowski pointed out the hazard of trying to interpret a resultset from SELECT *.
It looks to me like you need the following sort of query.
SELECT r.id AS record_id, r.timestamp, r.rep_id, r.notes AS record_notes,
d.record_type, d.amount, d.bottles_sold
FROM records AS r
LEFT JOIN (
SELECT record_id, record_type, amount, bottles_sold FROM sales
UNION ALL
SELECT record_id, record_type, amount, bottles_sold FROM accounts_receivable
) AS d ON d.record_id = r.id
ORDER BY r.rep_id, r.id, r.timestamp, d.record_type DESC
This will present each rep's activity in order, interleaving the 'sales' and 'ar' records.
If you separately join the sales and accounts_receivable tables you'll get an unwanted combinatorial explosion. Doing UNION ALL prevents that.
Notice that you could use a single physical table for your sales and ar data.

SQL left join - limit number of rows returned

I have a working SQL statement, but there is one issue within it I can't solve.
When I left join my table sites_photos there can be multiple matches on sp.sites_id = s.id, but I want the table to only return 1. Is this possible.
SELECT s.*, sp.photo
FROM sites s
LEFT JOIN sites_photos sp
ON sp.sites_id = s.id
My output: 2 times id 30, but with different photo paths, I only want 1 returned for that id, or both bundled in one array.
Array
(
[0] => Array
(
[id] => 30
[url] => www.test.nl
[name] => Aleve
[date] => 2014-08-16
[cms_active] => Y
[archive] => N
[photo] => 2014080812365920120214103601number_1.jpg
)
[1] => Array
(
[id] => 30
[url] => www.test.nl
[name] => Aleve
[date] => 2014-08-16
[cms_active] => Y
[archive] => N
[photo] => 20140811021102news.jpg
)
)
You can do so,by using GROUP_CONCAT which will concatenate all the photos per site by and produces comma separated list of photos then you can use SUBSTRING_INDEX over result of GROUP_CONCAT to pick one photo,you can also add the criteria of order by in GROUP_CONCAT as GROUP_CONCAT(sp.photo ORDER BY sp.id DESC)
SELECT s.*, SUBSTRING_INDEX(GROUP_CONCAT(sp.photo),',',1) photo
FROM sites s
LEFT JOIN sites_photos sp
ON sp.sites_id = s.id
GROUP BY s.id

mysql query - how to find items not within certain dates

I'm pretty useless at SQL it seems and I'm trying to figure out what is the correct query to use.
I have a table Items and a table Reservations. An item can have many reservations and reservations are made between two dates.
I'm trying to create a search query which will return all the Items which don't have reservations between two user inputted dates.
the SQL I have at the minute looks like:
SELECT `Item`.`id`, `Item`.`user_id`, `Item`.`name`, `Item`.`description`, `User`.`id`, `User`.`username`, `User`.`password`, `User`.`email`
FROM `database`.`items` AS `Item`
LEFT JOIN `database`.`reservations` AS `ReservationJoin` ON (`ReservationJoin`.`item_id` = `Item`.`id` AND `ReservationJoin`.`start` >= '2013-07-17' and `ReservationJoin`.`finnish` <= '2013-07-20')
LEFT JOIN `database`.`users` AS `User` ON (`Item`.`user_id` = `User`.`id`)
WHERE ((`Item`.`name` LIKE '%projector%') OR (`Item`.`description` LIKE '%projector%')
AND `ReservationJoin`.`id` IS NULL
LIMIT 10
I'm doing this in cakephp 2.3 so the code looks like:
$this->paginate = array(
"conditions" => array(
"or" => array(
"Item.name LIKE" => '%'.$projector.'%',
"Item.description LIKE" => '%'.$projector.'%',
),
"ReservationJoin.id" => null,
),
"joins" => array(
array(
"table" => "reservations",
"alias" => "ReservationJoin",
"type" => "LEFT",
"conditions" => array(
"ReservationJoin.item_id = Item.id",
"ReservationJoin.checkin >= '{$start}' and ReservationJoin.checkout <= '{$finnish}'",
)
)
),
"limit"=>10
);
$data = $this->paginate('Item');
This isn't working and I think it's to do with the join not excluding the reservations properly. But I've not been able to figure out what the correct mysql is. Can a kind soul tell me what I should be using?
thanks
If something has a reservation between two dates, then one of the following is true:
It has a start date between the dates
It has an end date between the dates
It has a start date before the earlier date and an end date after the second one
The following query uses this logic in a having clause. The approach is to aggregate at the item level and ensure that the three above conditions are true:
SELECT i.`id`, i.`user_id`, i.`name`, i.`description`
FROM `database`.`items`i LEFT JOIN
`database`.`reservations` r
ON r.`item_id` = i.`id`
WHERE ((i.`name` LIKE '%projector%') OR (i.`description` LIKE '%projector%')
group by i.id
having max(start between '2013-07-17' and '2013-07-20') = 0 and
max(finish between '2013-07-17' and '2013-07-20') = 0 and
max(start < '2013-07-17' and finished > '2013-07-20') = 0
LIMIT 10;
Note that none matches are returned, because the conditions are treated as false when start and ned are NULL.
I think it may be easier for you in CakePHP to put all of the conditions in a WHERE clause. (This could also be done with some OUTER JOINs but it may be difficult to transcribe into CakePHP)
SELECT id, user_id, name, description
FROM items
WHERE ((name LIKE '%projector%') OR (description LIKE '%projector%'))
AND NOT EXISTS(
SELECT *
FROM reservations
WHERE items.id = reservations.item_id
AND (
'2013-07-17' BETWEEN start and finnish
OR
'2013-07-20' BETWEEN start and finnish
OR
start BETWEEN '2013-07-17' AND '2013-07-20'));
Or, using the same logic the WHERE clause can be cleaned up to be
SELECT id, user_id, name, description
FROM items
WHERE ((name LIKE '%projector%') OR (description LIKE '%projector%'))
AND NOT EXISTS(
SELECT *
FROM reservations
WHERE items.id = reservations.item_id
AND NOT(
'2013-07-17' > finnish
OR
'2013-07-20' < start
));
you can try this.
SELECT `Item`.`id`, `Item`.`user_id`, `Item`.`name`, `Item`.`description`, `User`.`id`, `User`.`username`, `User`.`password`, `User`.`email`
FROM `database`.`items` AS `Item`
LEFT JOIN `database`.`reservations` AS `ReservationJoin` ON (`ReservationJoin`.`item_id` = `Item`.`id`)
LEFT JOIN `database`.`users` AS `User` ON (`Item`.`user_id` = `User`.`id`)
WHERE ((`Item`.`name` LIKE '%projector%') OR (`Item`.`description` LIKE '%projector%'))
AND `ReservationJoin`.`start` >= '2013-07-17'
AND `ReservationJoin`.`finnish` <= '2013-07-20'
AND `ReservationJoin`.`id` IS NULL
LIMIT 10
i.e just move the date clauses into the WHERE clause as oppose to being in the JOIN CLAUSE

Translate this monsterous query w/HABTM related data into a CakePHP find

It's late and I have written this monstrosity of a query to get related products based on a product I have already found.
I need to fetch the products in the same category (HABTM), the parent product, products with the same parent (siblings/neighbours), and products that are direct children of the current product (there is only one level of nesting). I have the product ID and its parent_id of the current product. If it could be possible to put conditions on the product as for Product.published = 1 that would be great, but if it's going to make the query so big I can always check that after. Additionally, I need to exclude the current product.
SELECT `products`.*
FROM `products`, `categories_products`
WHERE
(
(
`categories_products`.`product_id` = `products`.`id`
AND `categories_products`.`category_id` IN (
SELECT `category_id`
FROM `categories_products`
WHERE `categories_products`.`product_id` = '$product_id'
)
)
OR `products`.`parent_id` = '$parent_id'
OR `products`.`parent_id` = '$product_id'
OR `products`.`id` = '$parent_id'
)
AND `product`.`id` <> '$product_id'
GROUP BY `products`.`id`
It might even be possible to optimize it a bit more, so far I have:
public function related($productData, $limit = 4) {
$conditions = array(
'OR' => array(array('Product.parent_id' => $productData['Product']['id'])), // Children of product),
'Product.id <>' => $productData['Product']['id']
);
if(!empty($product['parent_id'])) {
$conditions['OR'][] = array('Product.parent_id' => $productData['Product']['parent_id']); // Siblings
$conditions['OR'][] = array('Product.id' => $productData['Product']['parent_id']); // Parent of product
}
return $this->find('all', array(
'conditions' => $conditions,
'contain' => array('Category'),
'group' => 'Product.id',
'limit' => $limit
));
}
You will need to use cake's Complex Find Conditions syntax (scroll down to Sub-queries secrion).
To be honest I didn't think its the best approach to get "related" products from mysql, thats what search engines are for. especially your similar category approach is would doom to fail when dealing with big data.
After saying this much this is a a re-write of your current sql which would hopefully have you gain some performance.
SELECT * FROM products p
WHERE
p.published = 1 AND
p.id != $product_id AND
(
p.id IN
(
SELECT DISTINCT(cp2.product_id) FROM categories_product cp1
LEFT JOIN categories_product cp2 ON cp1.category_id = cp2.category_id
WHERE cp1.product_id = $product_id
UNION SELECT $parent_id
)
OR p.parent_id IN($parent_id, $product_id)
)
;
I tried to get rid of unnecessary group by statement. Hope this helps.
P.S : There can be syntax errors since I wrote this in text editor.
I would prevent the need of the recursive attempt to SELECT IN on the category. Pre build that based on the one product in question and get all its distinct categories. From that, get distinct products that match the category. Now, you have a prequery of "CommonByCategory" that will ALREADY be a single instance of IDs.
Next, do a hard join to products again "OriginalProduct" based on the SPECIFIC ID you are trying to qualify against. Since it will always exist and never change, we can use this as the pointer for the siblings to compare against, and also for a parent ID match (in case not null -- via the IFNULL() tests applied
Since each product will only be scanned ONCE and not return multiple entries due to the multiple category possibilities, no "GROUP BY" is required.
SELECT STRAIGHT_JOIN
p.*
from
products p
left join
( SELECT DISTINCT
cp2.product_id
from
( SELECT cp.Category_ID
from categories_products cp
where cp.product_id = '$product_id' ) JustCats
join categories_products cp2
ON JustCats.Category_ID = cp2.Category_ID ) as CommonByCategory
ON p.ID = CommonByCategory.product_ID
join products OriginalProduct
ON OriginalProduct.ID = '$product_id'
where
p.id <> '$product_id'
and ( IFNULL( CommonByCategory.Product_ID, -1) > 0
OR p.id = IFNULL( OriginalProduct.Parent_ID, -1 )
OR p.parent_id = OriginalProduct.id
This issue came round again, and I figured it out 100%! Here was my finished code:
// Model
public function related($product, $limit = 9) {
// Children of product
$conditions = array(
'OR' => array(array('Product.parent_id' => $product['Product']['id'])), // Children of product),
'Product.id <>' => $product['Product']['id'],
'Product.published' => 1
);
// Siblings and parent of product if applicable
if (!empty($product['Product']['parent_id'])) {
$conditions['OR'][] = array('Product.parent_id' => $product['Product']['parent_id']);
$conditions['OR'][] = array('Product.id' => $product['Product']['parent_id']);
}
// Products in the same categories
// Get category IDs in an array
$categoryIds = Set::extract($product['Category'], '{n}.id');
$conditionsSubQuery['category_id IN(?)'] = implode(',', $categoryIds);
$db = $this->getDataSource();
$subQuery = $db->buildStatement(
array(
'fields' => array('product_id'),
'table' => 'categories_products',
'joins' => array(),
'alias' => 'c_p',
'conditions' => $conditionsSubQuery,
'order' => null,
'group' => null,
'limit' => null
), $this->CategoryProduct
);
$subQuery = 'Product.id IN (' . $subQuery . ') ';
$subQueryExpression = $db->expression($subQuery);
$conditions['OR'][] = $subQueryExpression;
return $this->find('all', array(
'conditions' => $conditions,
'contain' => array('Category'),
'group' => 'Product.id',
'limit' => $limit
));