Get the last record in a belongsToMany association - mysql

I have a belongsToMany association between Appointment and Status models. The below query returns all of the statuses and I want to alter it to pull the last status assigned to the appointment.
$query = Appointment::query();
$query->with('statuses');
$query->with("agent");
$query->with("instruction_type");
$query->with("sign_up_customer");
$table = Datatables::of($query);
I have tried altering the query with this but it doesn't work.
$query->with('statuses')->latest();
This is my raw query:
select * from `users` where `users`.`id` = '1' limit 1
select count(*) as aggregate from (select '1' as `row_count` from `appointments`) count_row_table
select * from `appointments` limit 100 offset 0
select `statuses`.*, `appointment_status`.`appointment_id` as `pivot_appointment_id`, `appointment_status`.`status_id` as `pivot_status_id` from `statuses` inner join `appointment_status` on `statuses`.`id` = `appointment_status`.`status_id` where `appointment_status`.`appointment_id` in ('2') order by `created_at` desc
select * from `agents` where `agents`.`id` in ('1')
select * from `instruction_types` where `instruction_types`.`id` in ('1')
select * from `organisations` where `organisations`.`id` in ('1')
So this works, but it runs two queries on Statuses
$query = Appointment::with(['statuses' => function ($query) {
$query->latest()->first();
}]);
$query->with("agent");
$query->with("instruction_type");
$query->with("sign_up_customer");
$table = Datatables::of($query);

In order to get the latest record from the database, you should have a created_at column to reach that. In this case, you can do something like that:
Appointment::with(['statuses' => function ($query) {
$query->orderBy('created_at', 'desc')->first();
}])->get();

try
$query->with(["statuses"=>function($q){
$q->latest();
}])->get()
not tested but guess it should work

Related

Zf2 table gateway join query to fetch only the latest row(by id) from second table

Hi, I need a zf2 join query that fetches only the latest row(by id DESC) from the second table. I have written an sql query and it works.
SELECT st1.customer_id,
st1.id
FROM status st1
inner JOIN
(
SELECT max(id) MaxId, customer_id
FROM status
GROUP BY customer_id
) st2
ON st1.customer_id = st2.customer_id
AND st1.id = st2.MaxId
But I need this query at zend framework 2 table gateway format. Please help.
use Zend\Db\Sql\Select;
use Zend\Db\Sql\Expression;
$sql = new Select ();
$sql->columns(["customer_id", new Expression ("max(id) AS MaxId")])
->from ('status')
->group('customer_id');
$outer = new Select ();
$outer->columns (['customer_id', 'id'])
->from (['st1' => 'status'])
->join (['st2' => $sql],
'st1.customer_id = st2.customer_id AND st1.id = st2.MaxId', []);

Getting data from two tables where one table is independent of the other

EDIT: Let me start over since I can't remove my post
I have three tables
messages
messages_share
users
I have two queries
query (1) gets the data from table messages
SELECT
`messages`.*, `users`.`uid`, `users`.`username`, `users`.`full_name`, `users`.`profile`
FROM
`messages`
LEFT JOIN
`users` ON `messages`.`owner_id`=`users`.`uid`
WHERE
`messages`.`to_id` IN ($ids)
ORDER BY
`messages`.`time` DESC
LIMIT
$start, $min
query (2) gets the data from messages_share
SELECT
`messages`.*, `messages_share`.*, `users`.`uid`, `users`.`username`, `users`.`full_name`, `users`.`profile`
FROM
`messages_share`
LEFT JOIN
`users` ON `messages_share`.`share_owner_id`=`users`.`uid`
LEFT JOIN
`messages` ON `messages`.`share_message_id`=`messages`.`id`
WHERE
`messages_share`.`share_to_id` IN ($ids)
ORDER BY
`messages_share`.`time` DESC
LIMIT
$start, $min
I need to combine these queries together such that depending on which table it grabs from, the LEFT JOIN users gets pulled from the correct table, and the ORDER BY gets sorted by both messages and messages_share
The sudo query should look like this
SELECT
if(table_we_are_pulling_from=`messages_share`)
`messages_share`.*,
`messages`.*, `users`.`uid`, `users`.`username`, `users`.`full_name`, `users`.`profile`
FROM
`messages_share`,
`messages`
if(table_we_are_pulling_from=`messages_share`)
LEFT JOIN `users` ON `messages_share`.`share_owner_id`=`users`.`uid`
else if(table_we_are_pulling_from=`messages`)
LEFT JOIN `users` ON `messages`.`owner_id`=`users`.`uid`
WHERE
if(table_we_are_pulling_from=`messages_share`)
`messages_share`.`share_to_id` IN ($ids)
else if(table_we_are_pulling_from=`messages`)
`messages`.`to_id` IN ($ids)
ORDER BY
`messages`.`time` DESC
`messages_share`.`share_time` DESC
LIMIT
$start, $min
If you're using PHP, you can simply build this query dynamically.
$table_we_are_pulling_from = '`messages_share`'; //or `messages`
$other_table = '`messages`';
$table_id = '`share_owner_id`'; // or '`owner_id`'
$to_id = '`share_to_id`'; // or '`to_id`';
$message_time = '`share_time`';
$query = " SELECT $table_we_are_pulling_from.*, `users`.`uid`, `users`.`username`, `users`.`full_name`, `users`.`profile`
FROM
$table_we_are_pulling_from
LEFT JOIN `users` ON $table_we_are_pulling_from.$table_id = `users`.`uid`
WHERE
$table_we_are_pulling_from.$to_id IN ($ids)
ORDER BY
$table_we_are_pulling_from.$messages_time DESC
LIMIT
$start, $min
Another option, if you're doing OOP, is to build an interface, implement it with two objects, and based on criteria that you set elsewhere, the object that will return the correct query will be selected. For instance,
interface Messages { public function getUsersQuery(); }
class NormalMessages implements Messages {
public function getUsersQuery() { //return query for regular messages }
}
class SharedMessages implements Messages {
public function getUsersQuery() { //return query for shared messages }
}
Then, in your code...
if (//some condition) $messages = new SharedMessages();
$query = $messages->getUsersQuery();

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

mySQL Query get record that dont match

I have an sql query that I wish to add another condition to but I cant seem to get it work. The query is simple enough:
SELECT DISTINCT monthly_returns.company_id
FROM monthly_returns, paidreturns
WHERE monthly_returns.company_id = paidreturns.company_id
AND paidreturns.month = '$cdate'
AND paidreturns.paid =0
However I wish to get the records also from the monthly_returns that have not record at all in paidreturns for the give date. I know it would be something like this
SELECT *
FROM monthly_returns
WHERE monthly_returns NOT IN (SELECT * FROM paidreturns WHERE paidreturns.month = '$cdate')
paidreturns.paid =0 is where the bill has not been paid, but equally if there is no record for that date in paidreturns then the bill is also not paid.
The schema
paidreturns table
-id
-company_id
-paid
-month
-total
monthly_returns table
-id
-company_id
-wage_totals
-month
Try this:
SELECT
DISTINCT monthly_returns.company_id
FROM
monthly_returns
LEFT JOIN
paidreturns
ON monthly_returns.company_id = paidreturns.company_id
AND monthly_returns.month = paidreturns.month
WHERE
monthly_returns.month = '$cdate'
AND
(
paidreturns.paid = 0
OR
paidreturns.company_id IS NULL
);
Using a LEFT JOIN, you can find all records from monthly_returns, regardless of whether they matched an entry from paidreturns.
Then, by adding paidreturns.company_id IS NULL to the WHERE clause, you include those unmatched entries in your query.
select company_id
from
(
(
SELECT DISTINCT monthly_returns.company_id
FROM monthly_returns, paidreturns
WHERE monthly_returns.company_id = paidreturns.company_id
AND paidreturns.month = '$cdate'
AND paidreturns.paid =0
)
union
(
SELECT company_id
FROM monthly_returns
WHERE monthly_returns NOT IN (SELECT * FROM paidreturns WHERE paidreturns.month = '$cdate'
)
) as x

combing two SELECT statements into a JOIN

I have two tables: table_1: item_id, name ... table_2: image_id, item_id, url
The following function retrieves the 'urls' of the items with the '$name' that is passed in. I first need to get the 'item_id' from table_1 to use in the query. I only need to return the 'url's from table_2.
It currently works fine, but I feel it could be streamlined to be done in one query with a JOIN rather than two separate queries. I've tried using different 'JOIN's to make it work but I can't seem to get it right.
Here's the function as it stands now with the two queries...
function get_items( $name ) {
global $wpdb;
$sql1 = "SELECT `item_id` FROM `table_1` WHERE `name` = '$name'";
$results1 = $wpdb->get_var( $sql1 );
$sql1 = "SELECT `url` FROM `table_2` WHERE `item_id` = '$results1'";
$results2 = $wpdb->get_results( $sql1, ARRAY_A );
return $results2;
}
and here is the 'JOIN' that I tried implementing, but was unsuccessful (I've also switched it around and did a 'LEFT/RIGHT JOIN' as well resulting in the same result) ...
$sql1 = "SELECT `table_2`.`url`
FROM `table_2`
INNER JOIN `table_1`
ON `table_2`.`item_id` = `table_1`.`item_id`
WHERE `table_1`.`item_id` = '$name'";
Any advice on combining these two queries?
The problem with your query is this WHERE table_1.item_id = '$name', it should be the name not the item_id
SELECT b.url
FROM table1 a
INNER JOIN table2 b
ON a.item_id = b.item_id
WHERE a.name = '$name'
select url from table_2 where item_id IN (select item_id from table_1 where name = $name )