How to retrieve nested join select statement data from query - php

I have the following query and am unsure of how to retrieve the 'note' from the nested join query.
This Left join selects the latest note for this customer, but I am not sure how to echo this data...
LEFT JOIN (SELECT note AS latestnote, timestamp, renewalid FROM renewal_note ORDER BY timestamp DESC LIMIT 1) AS n ON n.renewalid=renewal.id
Full Query:
SELECT renewal.id AS rid, renewal.personid, renewal.enddate, renewal.assettype, renewal.producttype, renewal.vrm, renewal.make, renewal.model, renewal.submodel, renewal.derivative, renewal.complete, person.forename, person.surname, person.company, appointment.id AS appid, appointment.renewalid,
(SELECT COUNT(complete) FROM renewal WHERE complete=1 && enddate BETWEEN '".$month_start."' AND '".$month_end."' && dealershipid='".$dealership_id."' && assettype='U' && producttype!='CH' && complete=1) AS renewedcount
FROM renewal
LEFT JOIN person ON person.id=renewal.personid
LEFT JOIN appointment ON appointment.renewalid=renewal.id
LEFT JOIN (SELECT note AS latestnote, timestamp, renewalid FROM renewal_note ORDER BY timestamp DESC LIMIT 1) AS n ON n.renewalid=renewal.id
WHERE enddate BETWEEN '".$month_start."' AND '".$month_end."' && renewal.dealershipid='".$dealership_id."' && assettype='U' && producttype NOT LIKE '%CH%'
ORDER BY enddate ASC
The Below is currently what is returned in each loop (which is working fine), and I can access as normal; $row['COLUMNNAME'].
rid
personid
enddate
assettype
used/new
producttype
vrm
make
model
submodel
derivative
complete
forename
surname
company
appid
renewalid
renewedcount
BUT I also need to be able to get the NOTE from renewal_note (the third LEFT JOIN).
I dont have issues with normal inner joins, But I have never created a query with subqueries, so struggling to echo this data out within the php loop.
(Just to point out that the only reason I am nesting a query is that I need only the latest note for each customer to be returned.)
I have tried $row['latestnote'], (with no success) and I am sure this is definitely not the way to access this data.
Could someone please point me in the right direction?
UPDATE
With updates from the comments, I have tried a much more simplified query (with ALL columns included so can't miss anything out) specifically targetting the subquery:
SELECT
*
FROM
renewal
LEFT JOIN(
SELECT
*
FROM
renewal_note
ORDER BY
TIMESTAMP
DESC
LIMIT 1
) AS n
ON
n.renewalid = renewal.id
But this still returns NULL for every column on the renewal_note.
90% of 'renewal' records have a note linked to them in the renewal_note table, but none showing.
renewal table has a unique primary key; ID.
renewal_note table links via column name: renewalid.

I think I have sussed this out.
Thanks to the commenters :)
Purely posting this for anyone else with similar questions/issues.
To get data from a LEFT JOIN(SELECT... statement; include the alias into the outer SELECT statement. Then you can use $row['columnname'] as normal.
LIMIT 1 on the subquery returned only 1 record for the entire query. Remove LIMIT 1 to show all results for the subquery dependant on your 'link' to the main query.
From what I have tested, ORDER BY seems to be working, but not sure if this is a fluke, or if it just sorting naturally by the primary key; ID

Related

query group by with order by

i need to create query with group by and order by, and i dont know how to do it.
query should return one record for the newest date for existing device_serial_number. enter image description here
so i would to get id 591 nad 592
solution can be in sql or the best way it will be in symfony, through query builder etc.
There are many ways to accomplish what you want.
First Way
The oldest way to select first, best, worst, whatever within a group is with a correlated subquery:
Select * from mytable outer
Where created_at = (
Select max(created_at)
from mytable inner
Where inner.device_serial_number = outer.device_serial_number
)
Second Way
Use a subselect to find earliest dates for all devices, them join back to the original table to filter:
Select a.*
From mytable a Inner Join
(Select device_serial_number, max(created_at) as latedate
From mytable b
Group By device_serial_number
) b
On a.device_serial_number=b.device_serial_number
And a.created_at=b.latedate
Third way
Use a window function to rank order all the dates and then pick the number one ranking.
Select * From (
Select *
, rank() Over (Partition By device_serial_number Order by created_at desc) as myrank
From mytable
)
Where myrank=1
Notice that while these 3 solutions use different aspects of SQL, they all have a common analytical approach. They are all two step processes whose first (inner) part involves finding the most recent created_at date for each device_serial_number and then reapplying that result back to the original table in the second (outer) part.

Select all but rows which match across two tables in query

What I'm trying to do
I have two tables, one for events, and one that says whether each event id has been seen. I want to create a query that joins both tables on event.id and seen.eventid. However, I would only like to return the rows which have not been seen, ordered by how recent they are.
What I have right now
This is the query which works for returning all the events which have already been seen:
SELECT *
FROM `events`
JOIN `seen` ON (`seen`.`event_id`=`event`.`id`)
//This is not part of the question but I might as well paste the entire code
//WHERE `user_id`='34'
//AND (`meta_id`='45' OR `meta_id`='37' OR `meta_id`='43')
GROUP BY `event_id`
ORDER BY `event`.`date` DESC
How do I reverse the query so that those returned are the rows in event that are not matched by this query?
We don't know your table structures so I'm just guessing...
SELECT *
FROM events e
LEFT
JOIN seen s
ON s.event_id = e.id)
AND s.user_id=34
WHERE e.meta_id IN (45,37,43)
AND s.event_id IS NULL
GROUP
BY e.event_id
ORDER
BY e.date DESC

Mysql - How to get a row number after Order by?

Let's say I have a table with the following columns:
p_id
userid
points
Let's say these columns have over 5000 records. So we actually have users with points. Each user has an unique row for their point record. Imagine that every user can get points on the website by clicking somewhere. When they click I update the database with the points they get.
So we have a table with over 5000 records of people who have points, right? Now I would like to order them by their points (descending), so the user with the most point will be at the top of the page if I run a MySQL query.
I could do that by simply running a query like this:
SELECT `p_id` FROM `point_table` ORDER BY `points` DESC
This query would give me all the records in a descending order by points.
Okay, here my problem comes, now (when it is ordered) I would like to display each user which place are they actually. So I'd like to give each user something like this: "You are 623 of 5374 users". The problem is that I cannot specify that "623" number.
I would like to run a query which is order the table by points it should "search" or count the row number, where their records are and than return that value to me.
Can anyone help me how to build a query for this? It would be a really big help. Thank you.
This answer should work for you:
SET #rank=0;
SELECT #rank:=#rank+1 AS rank, p_id FROM point_table ORDER BY points DESC;
Update: You might also want to consider to calculate the rank when updating the points and saving it to an additional column in the same table. That way you can also select a single user and know his rank. It depends on your use cases what makes more sense and performs better.
Update: The final solution we worked out in the comments looked like this:
SELECT
rank, p_id
FROM
(SELECT
#rank:=#rank+1 AS rank, p_id, userid
FROM
point_table, (SELECT #rank := 0) r
ORDER BY points DESC
) t
WHERE userid = intval($sessionuserid);
Row number after order by
SELECT ( #rank:=#rank + 1) AS rank, m.* from
(
SELECT a.p_id, a.userid
FROM (SELECT #rank := 0) r, point_table a
ORDER BY a.points DESC
) m
For some reason the accepted answer doesn't work for me properly - it completely ignores "ORDER BY" statement, sorting by id (primary key)
What I did instead is:
SET #rn=0;
CREATE TEMPORARY TABLE tmp SELECT * FROM point_table ORDER BY points DESC;
SELECT #rn:=#rn+1 AS rank, tmp.* FROM tmp;
Add a new column for position to the table. Run a cron job regularly which gets all the table rows ordered by points and then update the table with the positions in a while loop.

php mysql inner join - get a particular row

This is my query to get model information from one table and a single picture from another table. What changes do I have to make to this query in order for it to get the picture where ORDER BY sort DESC? In the table of the pictures, there is a field by the name "sort". The default value for the field for each row is 0. But one random row has the value of 1. I want to get that particular row. I don't, however, want to use WHERE sort=1 because then even in the case where no row has the sort value 1, one row should still get fetched.
$sql="SELECT tc.id,tc.alias,tc.firstname,tci.imagename
FROM ".$pre."models tc
INNER JOIN ".$pre."model_images tci ON tc.id=tci.userid
WHERE activated=1 AND sex=$sex AND city=$city AND published=1
GROUP BY tc.id ORDER BY firstname ASC";
Thank you in advance!
Solved using:
SELECT tc.id,tc.alias,tc.firstname,
(SELECT imagename FROM ".$pre."model_images WHERE userid= tc.id
ORDER BY sort DESC LIMIT 1) AS imagename
FROM ".$pre."models tc
WHERE tc.activated=1 AND tc.sex=1 AND tc.city=2 AND tc.published=1
ORDER BY tc.firstname ASC
You should place that in your WHERE clause aswell. One t hing to note though is to be carefull with the way you're using the column names. It's better to tell to which table they belong.
So this:
WHERE activated=1 AND sex=$sex AND city=$city AND published=1
Should be:
WHERE tc.activated=1 AND tc.sex=$sex AND tc.city=$city AND tc.published=1
And then simply add the 'sort' column to it:
WHERE tc.activated=1 AND tc.sex=$sex AND tc.city=$city AND tc.published=1 AND tci.sort=1
If no results are returned, then make sure that there are records that meet the required conditions. Because there's nothing wrong with the query. Try to print your query to the screen etc. to see if every variables has a value.
edit:
You should lose the GROUP BY.
SELECT tc.id,tc.alias,tc.firstname,tci.imagename
FROM ".$pre."models tc
INNER JOIN ".$pre."model_images tci ON tc.id=tci.userid
WHERE tc.activated=1 AND tc.sex=$sex AND tc.city=$city AND tc.published=1 AND tci.sort=1

SQL order by, group by, having

I'm using a database to store results of an election with the columns id, candidate, post_time and result. Results are put in the database during 'counting the votes'. When a new update is available, a new entry will be inserted.
From this database, I would like to create a table with the most recent results (MAX post_time) per candidate (GROUP BY candidate), ordered by result (ORDER BY result).
How can I translate this to a working SQL-statement?
(I've tried mysql order and groupby without success)
I've tried:
SELECT *, MAX(time_post)
FROM [database]
GROUP BY candidate
HAVING MAX(time_post) = time_post
ORDER BY result
Assuming that you don't have multiple results per candidate at same time, next should work:
select r.candiate, r.result
from results r
inner join (
select candidate, max(post_time) as ptime
from results
group by candidate
) r2 on r2.candiate=r.candidate and r2.ptime=r.post_time
order by r.result
Note that MAX will not select the record with the maximum time, but it will select the maximum value from any record. So
SELECT MAX(a), MAX(b) FROM example
where exmple contains the two records a=1, b=2 and a=4, b=0, will result in a=4, b=2, which wasn't in the data. You should probably create a view with the latest votes only from each candidate, then query that. For performance, it may be sensible to use a materialized view.
Is the post_time likely to be the same for all the most recent results? Also does each candidate only appear once per post_time?
This could be achieved by just using a SELECT statement. Is there a reason you need the results in a new table?
If each candidate only appears once per post_time:
SELECT candidate, result
FROM table
WHERE post_time = (SELECT MAX(post_time) FROM table)
If you want to count how many times a candidate appears in the table for the last post_time:
SELECT candidate, count(result) as ResultCount
FROM table
WHERE post_time = (SELECT MAX(post_time) FROM table)
GROUP BY candidate
By what i see from ur attempts i'd think you should use this
SELECT MAX(post_time) FROM `table` GROUP BY candidate ORDER BY result
but the MAX statment only return a single value therefore i dont see why ORDER BY would be needed.
if you want multiple results try looking up the TOP statment
One way (tied results shown):
SELECT t.*
FROM tableX AS t
JOIN
( SELECT candidate
, MAX(time_post) AS time_post
FROM tableX
GROUP BY candidate
) AS m
ON (m.candidate, m.time_post) = (t.candidate, t.time_post)
ORDER BY t.result
and another one (no ties, only one row per candidate shown):
SELECT t.*
FROM
( SELECT DICTINCT candidate
FROM tableX
) AS d
JOIN
tableX AS t
ON t.PK = --- the Primary Key of the table, here
( SELECT ti.PK --- and here
FROM tableX AS ti
WHERE ti.candidate = d.candidate
ORDER ti.time_post DESC
LIMIT 1
)
ORDER BY t.result

Categories