简体   繁体   中英

Mysql return 2 linked items, ordered by created and grouped by table id

Been trying to think up the best way to do this but so far I have been stumped.

Basically in 1 query I want to get data from 2 tables but limit them in an unorthodox way.

table1 is a parent of table2 . I need 2 table2 rows returned for every 1 table1 row.

So in data it looks like this:

Row 1
  Item 1
  Item 2
Row 2
  Item 1
  Item 2 

But the actual mysql data is something like this:

Row 1 Item 1
Row 1 Item 2
Row 2 Item 1
Row 2 Item 2

This is easy enough if there is no limitation on how much items per table1 row there are, but I need to limit them to 2, order the items by the created date, and group them by the table1 id.

I am using Laravel for this, but I know this is a situation where DB:raw is used, here is the concept of what I got.

$data = DB::table('table2')
    ->select('table2.data', 'table2.created_at')
    ->leftJoin('table1', 'table2.part_id', '=', 'table1.id')
    ->orderBy('table2.created_at')
    ->groupBy('table1.id');

I am thinking do a raw join and try to do a table subquery for it to return 2 of every table1 item but never had to try to create duplicate items deliberately before and I feel it would be totally the wrong way to go about it.

Anyone got any thoughts on this?

Oh and also to explain why I am not using a model for this. Currently this works using normal Eloquent models, however Eloquent does multiple queries and processes the data on the php side, this specific query churns out ALOT of data, so in order to keep it efficient I want to try to do it all in 1 query (and its hitting php max execution time).

Here is a sample of the eventual output

Item 1 
  previous (created at) - data
  current (created_at) - data
Item 2
  previous (created at) - data
  current (created_at) - data

So the "sub items" are basically logs, they track "work" done to the item, I need to the get the current and the previous one for reports, however using the test data I have right now that is about 20,001 queries in 1 page load.

So this solution I attempted is very improper. I do not believe you are using queries the way they were designed and that is what the biggest problem is. You should consider tracking this data differently than what is currently being tracked.

I would do something along the lines of the updated_at being the most recent update to the element. I would then have a history table that would only record the previous most item according to the id. If you needed to save more data than that then it would most likely be ok to continue to inputing data into this history, but then you would only ever need to pull the most recent id. Understandably you have a large set of predefined data, but if you can change it this is the route I would seriously consider.

Problems that I do not see a solution to are:

  • The inability to limit the query in a way to get only 2 rows defined by X while until X ends as we talked about can be done in PHP so it seems to work!
  • Inefficiency is very likely to occur. apparently expected
  • \n
  • Lost logic. (not 100% sure why you started off this way)

I wish I could help more, but I can't think of a solution within the parameters of SQL or an efficient loop in PHP to keep the structure you have defined and get the correct results.

If using PHP the loop will not be that efficient, but it will be right. You found a solution that works well for your standards.

\n

 
 
 
  
  $data = DB::table('table2') ->select(DB::raw('SELECT table2.data,table2.created_at FROM table2 WHERE table2.part_id=table1.id ORDER BY table2.created_at DESC LIMIT 2')) ...
 
  

I don't have the opportunity to try this, but can you use this raw query in your execution and see if it does the correct thing. This should cover it, but I do believe you are right that a regular Query Build will not do this correctly.

I have been playing around in HeidiSQL to get a query that works, this seems to get close enough that I can use.

select 
    distinct `table2`.`created_at`, 
    `table2`.`points`,
    `table1`.`id`
from `table2`
inner join `table1`
on ( `table1`.`id` = `table2`.`part_id` )

It does not limit to 2, but is simple enough that it should run quickly.

Originally I had a sub query, but I realized that there was no need for it if I remove the thought of limiting the joined tables results.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM