Mysql join with latest update in 4 tables - php

Hi here is my tables..
Table Sites
sid sname uid
---- ---------- ----
1 aaa.com 1
5 bbb.com 1
Table keywords_s
kid skeywoird
---- ----------
1 word1
2 word2
Table matchon
mid uid sid kid
---- ------ ----- -----
1 1 1 1
2 1 1 2
Table rank
mid rank dateon url
---- ------ ------- -----
2 7 08-May-2014 bbb.com/a
2 6 09-May-2014 bbb.com/2
And my query
"SELECT
keywords_s.skeyword,
keywords_s.kid,
sites.sname,
rank.rank,
rank.url,
rank.dateon
FROM matchon
Inner JOIN sites ON sites.sid = matchon.sid
Inner JOIN keywords_s ON keywords_s.kid = matchon.kid
Inner JOIN rank ON rank.mid = matchon.mid
where matchon.uid = :uid and sites.sname = :sname and sites.deactive != '1'
group by keywords_s.skeyword order by rank.rank
"
I am getting output
rank keyword dateon url
---- --------- ------- -----
7 word2 08-May-2014 bbb.com/a
Output needed is
rank keyword dateon url
---- --------- ------- -----
6 word2 09-May-2014 bbb.com/2
Here what i want to get ...
1st Group by keywords_s.skeyword order by rank.rank (this is coming But)
2nd Order by rank.slno desc (not working)
(I need 2nd order to work so i can get latest rank and date with, group by skeyword and order by rank)

SELECT keywords_s.skeywor
, keywords_s.kid
, sites.sname
, rank.rank
, rank.url
, rank.dateon
FROM matchon JOIN sites ON sites.sid = matchon.sid
JOIN keywords_s ON keywords_s.kid = matchon.kid
JOIN rank ON rank.mid = matchon.mid AND
rank.dateon = (SELECT MAX(dateon) FROM rank WHERE mid = matchon.mid)
WHERE matchon.uid = :uid and sites.sname = :sname and sites.deactive != '1'
GROUP BY keywords_s.skeyword order by rank.rank

This should work as per your requirement
"Select * from(SELECT
keywords_s.skeyword,
keywords_s.kid,
sites.sname,
rank.rank,
rank.url,
rank.dateon
FROM matchon
Inner JOIN sites ON sites.sid = matchon.sid
Inner JOIN keywords_s ON keywords_s.kid = matchon.kid
Inner JOIN rank ON rank.mid = matchon.mid
where matchon.uid = :uid and sites.sname = :sname and sites.deactive != '1'
order by rank.rank desc)xyz
group by xyz.skeyword
"

Related

How to get the latest rows for each user ID in laravel?

id user_id name qty datetime
--- --------- ---- ---- -----------
1 1 a 5 2019-12-01 12:26:01
2 2 b 3 2019-12-13 12:26:02
3 1 c 4 2019-12-13 12:26:03
4 2 a 2 2019-12-25 12:26:04
5 1 c 2 2019-12-21 12:26:06
i Want the this data
id user_id name qty datetime
--- --------- ---- ---- -----------
5 1 c 2 2019-12-21 12:26:06
4 2 a 2 2019-12-25 12:26:04
using laravel and also if possible then what will be the sql query for it
Models:
Users: id, name, email, etc...
Orders: user_id, qty, name, datetime etc..
Model Query:
Orders::orderBy('datetime', 'desc')->get()->unique('user_id');
DB Query
DB::table('orders')->orderBy('datetime', 'desc')->get()->unique('user_id');
In pure SQL, you can filter with a correlated subquery:
select t.*
from mytable t
where t.datetime = (
select max(t1.datetime) from mytable t1 where t1.user_id = t.user_id
)
or an uncorrelated subquery...
select x.*
from mytable x
join (
select user_id, max(t1.datetime) datetime from mytable group by user_id
) y
on y.user_id = x.user_id
and y.datetime = x.datetime

How to group output rows based on date either on mySQL or PHP?

The following query returns some rows, that each one has its own date_created.
SELECT *
FROM table1 l
inner join table2 d
ON d.id = l.id
WHERE l.company_id = 1
UNION ALL
SELECT *
FROM table1 l
inner join table3 p
ON p.id = l.id
WHERE p.company_id = 1
sample output:
company_id ---- user_id ---- text ---- dateAndTime
1 ---- abc ---- hi ---- 2017-08-17 22:26:18
1 ---- def ---- hey ---- 2017-08-08 11:57:26
1 ---- abc ---- hi ---- 2017-08-08 11:55:40
1 ---- ghz ---- sup ---- 2017-07-25 16:01:34
What I want to achieve:
I am running a php foreach loop to display all of these data. However on each date change, I want to display the date (like groupping them design-wise).
So the output (on display)
2017-08-17
1 ---- abc ---- hi ---- 2017-08-17 22:26:18
2017-08-08
1 ---- def ---- hey ---- 2017-08-08 11:57:26
1 ---- abc ---- hi ---- 2017-08-08 11:55:40
2017-07-25
1 ---- ghz ---- sup ---- 2017-07-25 16:01:34
How can I achieve this either with mySQL or PHP?
Store the last date in a variable, check in every iteration:
$q = "SELECT company_id, user_id, text, dateAndTime, DATE(dateAndTime) as date FROM table1 l inner join table2 d ON d.id = l.id WHERE l.company_id = 1 UNION ALL SELECT company_id, user_id, text, dateAndTime, DATE(dateAndTime) as date FROM table1 l inner join table3 p ON p.id = l.id WHERE p.company_id = 1";
// ...
$lastGroupDate = null;
foreach ($row as $company) {
if ($lastGroupDate !== $company["date"]) {
echo $company["date"]."<br>";
}
echo $company["user_id"]."<br>";
// rest of fields
$lastGroupDate = $company["date"];
}
Remember to sort the results by dateAndTime in the query.
Important: you need to get DATE(dateAndTime) as date in the query too, since you need to group them by only date (not time).

select one winner of highest score of every one month

I need to select highest scorer as a monthly winner. I want to show previous winners too. My current query selects only previous one month winner, but how can I select all previous monthly winners?
my query:
function month_winner_now()
{
$query = $this->db->query("SELECT winner.id,winner.score,winner.user_id,winner.date, user.id,user.username,user.email,user_profile.user_image,user_profile.hometown,user_profile.country FROM `winner` LEFT JOIN `user` ON user.id = winner.user_id LEFT JOIN `user_profile` ON user_profile.user_id = winner.user_id WHERE MONTH(CURDATE())= MONTH(winner.date) ORDER BY winner.score DESC
LIMIT 1");
return $query->result_array();
}
My current output :
"monthly winners":[
{
"id":"5",
"score":"1256",
"user_id":"5",
"date":"2014-03-05",
"username":"",
"email":"",
"user_image":"",
"hometown":"",
"country":""
}
But I need output like
"monthly winners":[
{
"id":"4",
"score":"233",
"user_id":"4",
"date":"2014-03-02",
"username":"Mahmudul Hasan Swapon",
"email":"",
"user_image":"",
"hometown":"",
"country":""
},
{
"id":"7",
"score":"123",
"user_id":"7",
"date":"2014-03-04",
"username":"Prosanto Biswas",
"email":"",
"user_image":"",
"hometown":"",
"country":""
}
],
Monthly winners json array shows previous all month winners but every month should have one winner.
DB table look like
id | name | userid | score | date |
------------------------------------------------------------
1 | john | 1 | 44 | 2013-03-2
2 | mary | 2 | 59 | 2013-03-5
3 | john | 12 | 38 | 2013-03-8
4 | elvis | 3 | 19 | 2013-02-10
5 | john | 11 | 1002 | 2013-01-11
6 | johnah | 10 | 200 | 2013-01-11
I recreated sql query and added one more field "month_of_year", now I think it will be helpful for you according to your requirement
SELECT
winner.id,winner.score,winner.user_id,winner.date,
user.id,user.username,user.email,user_profile.user_image,
user_profile.hometown,user_profile.country,
date_format( winner.date, '%Y-%m' ) AS month_of_year
FROM
`winner`
LEFT JOIN `user`
ON user.id = winner.user_id
LEFT JOIN `user_profile`
ON user_profile.user_id = winner.user_id
GROUP BY month_of_year
ORDER BY winner.score DESC
i want to show previous all month winner
You are checking for equality of month hence other months (previous or later) are omitted. Change comparison to <= or >= as the case may be.
And if you use LIMIT 1 there is a chance that other month details are not fetched if exists a record in current month.
Try this:
SELECT
winner.id -- etc fields
FROM
`winner`
LEFT JOIN `user`
ON user.id = winner.user_id
LEFT JOIN `user_profile`
ON user_profile.user_id = winner.user_id
WHERE
date_format( winner.date, '%Y%m' ) <= date_format( CURDATE(), '%Y%m' )
ORDER BY
winner.score DESC
For the winner from previous month:
$query = $this->db->query("
SELECT winner.id,winner.score,winner.user_id,winner.date, user.id,user.username,user.email,user_profile.user_image,user_profile.hometown,user_profile.country
FROM `winner`
LEFT JOIN `user`
ON user.id = winner.user_id
LEFT JOIN `user_profile`
ON user_profile.user_id = winner.user_id
WHERE MONTH(DATE_SUB(CURDATE(), INTERVAL 1 MONTH)) = MONTH(winner.date) ORDER BY winner.score DESC LIMIT 1
");
Try something like this query:
SELECT * FROM user
INNSER JOIN
(SELECT user_id, MAX(score) AS s, MONTH(date) AS d
FROM winner
GROUP BY MONTH(date)) monthlyWinner ON (user.id = monthlyWinner.user_id)
Try this,
;with cte as
(
select Datename(mm,[date]) as m
--,max(amount_paid)
,Rank() over(PARTITION BY Datename(mm,[date]) order by [score] desc) as rr
,[score]
,id
from myTbl
--where DATEDIFF(YY,[date],'1/1/2013') = 0
)
select * from cte
left join myTbl as r on r.id=cte.id
where rr = 1

PHP MySQL Highscore table

Im joining 3 tables to present a table with users highest score
My tables
game_log:
---ID---user_ID---score---time---
| 1 52 567 10 |
| 2 53 641 13 |
| 3 52 465 8 |
| 4 53 451 14 |
---------------------------------
users:
---ID---name---countyid---
| 52 Dave 1 |
| 53 John 2 |
------------------------
county:
---countyid---countyname---
| 1 Orange wichit |
| 2 Orange clemts |
--------------------------
SQL:
SELECT * FROM game_log
INNER JOIN users ON game_log.user_ID=users.ID
INNER JOIN county ON users.countyid=county.countyid
ORDER BY game_log.score DESC , game_log.time LIMIT 20";
Above code gives me this result:
Rank---Name--------County------Score---Time
1 John Orange clemts 641 13
2 Dave Orange wichit 567 10
3 John Orange clemts 465 8
4 Dave Orange wichit 451 14
My problem is that I want the highscore table to display the top 20 users with the highest score, not the 20 highest scores.
Like this:
Rank---Name--------County------Score---Time
1 John Orange clemts 641 13
2 Dave Orange wichit 567 10
Need som help with this, not familiar with joining tables ;-)
This approach will show the top 20 users and each user's highest score, and if they have multiple instances of the same score, it'll show the information for the earliest one (lowest time value for that user and score).
SELECT *
FROM game_log gl
INNER JOIN users u
ON gl.user_ID = u.ID
INNER JOIN county c
ON u.countyid = c.countyid
WHERE not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id
and gl2.score > gl.score)
and not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id
and gl2.time < gl.time
and gl2.score = gl.score)
ORDER BY gl.score DESC, gl.time LIMIT 20;
Without doing this, if the same user in the top 20 had the same score 2+ times, they would be listed 2+ times, and you would not get back 20 people by using LIMIT 20 because the same person would be taking up N rows out of that 20.
SQL Fiddle here showing data with a tie: http://sqlfiddle.com/#!2/0ac931/5/0
GROUP BY should do the job.
SELECT users.ID, users.name, county.countyname, MAX(game_log.score) AS score, game_log.time
FROM game_log
INNER JOIN users ON game_log.user_ID = users.ID
INNER JOIN county ON users.countyid = county.countyid
GROUP BY game_log.user_ID
ORDER BY game_log.score DESC, game_log.time
LIMIT 20;
Try it out with SQL Fiddle.
I would do this with the not exists approach to get the highest score for each user. The rest of the query is the same:
SELECT *
FROM game_log gl INNER JOIN
users u
ON gl.user_ID = u.ID INNER JOIN
county c
ON u.countyid = c.countyid
WHERE not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id and gl2.score > gl.score
)
ORDER BY gl.score DESC, gl.time
LIMIT 20;
The where clause is saying "keep this row if no other row for the same user has a higher score".
Another way to do this is with the aggregation approach:
SELECT *
FROM (select user_id, max(score) as maxscore
from game_log gl
group by user_id
) gl INNER JOIN
users u
ON gl.user_ID = u.ID INNER JOIN
county c
ON u.countyid = c.countyid
ORDER BY gl.maxscore DESC
LIMIT 20;
But this method loses the information about time. It is possible to include that, but it makes the query more complicated.

PDO select and count IN HAVING DISTINCT to appear in dropdown

I have this mysql select working great. It returns the proper data. I can't seem to get the context correct to place the actual count of the custnum so it will appear on the end of the dropdown option select.
This statement returns the proper location names
$statement = $pdo->prepare("SELECT locationname FROM location WHERE locationname IN (SELECT locationname FROM location_user WHERE custnum= :custnum GROUP BY locationname HAVING COUNT( DISTINCT email) < 6 )");
$statement->execute(array(':custnum' => $session->custnum));
while($row = $statement->fetch(PDO::FETCH_ASSOC)){
echo'<option value="'.$row['locationname'].'">'.$row['locationname'].'('. $row['COUNT(total)'] .')</option>';
}
Here's one of my attempts to grab the total for each custnum
$statement = $pdo->prepare("SELECT locationname, COUNT(custnum) AS total FROM location WHERE locationname IN (SELECT locationname FROM location_user WHERE custnum= :custnum GROUP BY locationname HAVING COUNT( DISTINCT email) < 6 )");
$statement->execute(array(':custnum' => $session->custnum));
while($row = $statement->fetch(PDO::FETCH_ASSOC)){
echo'<option value="'.$row['locationname'].'">'.$row['locationname'].'('. $row['total'] .')</option>';
}
Here's my tables
table location table location_user
custnum | locationname custnum | locationname | email | userlevel
1 location1 1 location1 1me#you.com 3
1 location2 1 location1 1me#you.com 1
1 location1 2me#you.com 2
1 location1 3me#you.com 2
1 location1 4me#you.com 2
1 location1 5me#you.com 2
1 location2 1me#you.com 2
1 location2 1me#you.com 3
The first select returns
location1()
location2()
The second select returns
location1(2)
I actually need the count of the distinct email which the query is doing and returning only the locationnames of the distinct email in the table less then 6 but how do I get the actual number of distinct emails for each locationname.
This select will retrieve the total for DISTINCT email, but how do I combine the two into one for my while loop?
$statement2 = $pdo->prepare("SELECT COUNT(email) AS total FROM location_user WHERE custnum= :custnum GROUP BY locationname HAVING COUNT( DISTINCT email) < 6");
$statement2->execute(array(':custnum' => $session->custnum));
Here's the working version from the help of Peter and a little prodding from Tin.
$statement = $pdo->prepare("SELECT l.locationname, COUNT(DISTINCT lu.email) AS total
FROM location l LEFT JOIN location_user lu ON l.locationname = lu.locationname AND l.custnum = lu.custnum WHERE l.custnum = :custnum GROUP BY l.locationname HAVING COUNT(DISTINCT lu.email) < 5 ");
$statement->execute(array(':custnum' => $session->custnum));
while($row = $statement->fetch(PDO::FETCH_ASSOC)){
echo'<option value="'.$row['locationname'].'">'.$row['locationname'].'('. $row['total'] .')</option>';
}
Here's another version that I'm working on to skip the user that adds the locations to the table. This user will always have a userlevel > 2. The uselevel is placed in the location_user table only as a value between 1-9. So I still need the location name but I don't want their location included in the count. I just realized that I could actually go a better route because the only email that I want to count will have a userlevel of 2. I was using the distinct email to filter out the userlevel of 1. I'll give it a go. The below version drops my locations that arn't in the location_user table but it's returning the proper count.
SELECT l.locationname, COUNT(lu.userlevel) AS total
FROM location l LEFT JOIN location_user lu
ON l.locationname = lu.locationname
AND l.custnum = lu.custnum
WHERE l.custnum = :custnum
AND lu.userlevel = 2
GROUP BY l.locationname
HAVING COUNT(lu.userlevel) < 6
UPDATE2: based on your comments. Try it this way
SELECT l.locationname, COUNT(DISTINCT lu.email) AS total
FROM location l LEFT JOIN location_user lu
ON l.locationname = lu.locationname
AND l.custnum = lu.custnum
AND lu.userlevel < 3 -- consider only users with user level < 3
WHERE l.custnum = ?
GROUP BY l.locationname
HAVING COUNT(DISTINCT lu.email) < 6
Sample output:
| LOCATIONNAME | TOTAL |
|--------------|-------|
| location1 | 5 |
| location2 | 1 |
| location3 | 0 |
Here is SQLFiddle demo
you don't actually need to query from table location since you already have locationname field from table location_user
SELECT locationname, count(DISTINCT email) as total FROM location_user WHERE custnum = :custnum GROUP BY locationname HAVING count(DISTINCT email) < 6

Categories