Facing difficulties to write a sql query in laravel - php

We have three tables users,answers and stretches. When someone login to our system we stored user_id,created_at and updated_at in stretches table and when he/she logout updated deleted at in the same row.
Session time 30 minutes.
users table
id
f_name
l_name
email
answers table
id
user_id
body
created_at
updated_at
stretches table
id
user_id
created_at
updated_at
deleted_at
Now i want to get only those rows from the stretches table where an answer has been given. I wrote this query but it takes to much time but did not return accurate data.
$query = "select count(a.id), a.user_id
from answers a
where a.created_at in
(select s.created_at
from stretches s
where s.created_at >= a.created_at
) group by a.user_id";
$results = DB::select(DB::raw($query));
I'd be curious to hear how some of you write this query.
Answers table
id | body | user_id | created_at | updated_at
10 | text | 10 | 2015-10-10 0:0:0 | 2015-10-10 0:0:0
Stretches Table
id | user_id | created_at | updated_at | deleted_at
1 | 10 | 2015-10-00 0:0:0 | 2015-10-10 0:0:0 | 2015-10-20 0:0:0
Now i want to get those rows from stretches table where an answer has been given.
Query results should be:
id | user_id | created_at | updated_at | deleted_at
1 | 10 | 2015-10-10 0:0:0 |2015-10-10 0:0:0 | 2015-10-10 0:0:0

If I correctly understood you. SQL query below will return only those records from table Stretches where answer was given to user. Use operator JOIN, it will do filter work for you.
SELECT S.user_id, COUNT(*) as answers_count FROM Stretches AS S
JOIN Answers AS A ON S.user_id = A.user_id
GROUP BY S.user_id

This should do the job
SELECT DISTINCT Stretches.* FROM Stretches
JOIN Answers ON Stretches.user_id = Answers.user_id
GROUP BY Stretches.user_id

Related

MySQL Specific condition for each row in same query +PHP

Please consider my tables
store - table with information of all my stores
zone - An alias table with zone_id and the name of the zone.
store_zone - Table that contains the information when a store was changed to a different zone.
cashdesk - table with local_id that contains information about the
income for each day and each store
store_zone table:
+----+----------+---------+-------------+
| id | store_id | zone_id | modified_at |
+----+----------+---------+-------------+
| 1 | 1 | 2 | 2019-01-01 |
| 2 | 1 | 1 | 2019-01-04 |
| 3 | 2 | 4 | 2019-01-03 |
+----+----------+---------+-------------+
store_id is the foreign key for my local table id
store table also contains a fk for zone_id
zone_id is the foreign key for my zone table id
Now consider my problem:
I need to select all stores
get the sum(income) per row (stores)
filter by zone
consider only incomes that correspond to the local_zone table between
dates. (if I select the whole month and a zone was changed the day
15.. I only care about the 15 first days). I can use php to help build it.
something like:
$zone_modified_dates = //result of
"SELECT modified_at
FROM store_zone
WHERE zone_id = 1 AND store_id = 1"
$zone_modified_dates = '2019-01-04'
SELECT sum(income) from cashdesk c
inner join store s ON s.id = c.store_id
inner join store_zone sz ON (sz.store_id = s.id AND sz.zone_id = z.id
WHERE zone_id = 1
AND cashdesk created_at >= '2019-01-01'
AND cashdesk created_at >= '2019-01-10'
AND //IF ZONE WAS CHANGED IN THIS CREATED_AT BETWEEN DATES ONLY CONSIDER THE DAYS THAT THE SPECIFIC ROW STORE WAS CONSIDERED ZONE 1
GROUP BY s.id
Thanks in advance.
#edited for better explanation of the problem.

Ranking SQL query

I'm making kind of top-ten ranking in my app, and I'm stuck in the SQL query that I'll use for that.
I have 2 tables.
The 'posts' table stores the ID of the post autor(user_id), and the post content(and, of course, the entry ID).
+----+---------+--------------+
| ID | user_id | content |
+----+---------+--------------+
| 1 | 3 | Lorem Ipsum1 |
| 2 | 6 | Lorem Ipsum2 |
| 3 | 3 | Lorem Ipsum3 |
+----+---------+--------------+
The 'likes' table, stores ID of the person who liked the post(user_id), the post ID(post_id) and the like date witch is a timestamp(like_date).
+----+---------+---------+------------+
| ID | user_id | post_id | like_date |
+----+---------+---------+------------+
| 1 | 2 | 1 | 1491484851 |
| 2 | 5 | 1 | 1491484871 |
| 3 | 11 | 2 | 1491484891 |
+----+---------+---------+------------+
Every time a user like a post, an entry is created at the 'likes' table, and if the user unlike it, I just remove the entry.
And here's the deal. I want to grab the top 10 most liked users of the last 30 days. I want the query result to be something like this
+---------+-------+
| user_id | likes |
+---------+-------+
| 3 | 2 |
| 6 | 1 |
+---------+-------+
I've already tried a tons of queries and spent a couple of hours trying to solve that, but I just cant figure out how to.
This one should do the trick
select user_id, count(*)
from posts t1
join likes t2
on t1.ID = t2.post_id
where from_unixtime(t2.like_date) >= now() - interval 30 day
group by user_id
order by count(*) desc
limit 10
Assuming you mean "post_id" not "user_id" in your expected results:
SELECT post_id, count(*) as 'likes'
FROM likes
GROUP BY post_id
WHERE like_date >= DATE(NOW() - INTERVAL 30 DAY)
ORDER By 2 desc
LIMIT 10
Everything's in the likes table, so filter recent posts (WHERE), return/order by the count and onl retrieve the first 10 rows. There are other ways to achieve this...
You don't even need a join for this:
SELECT user_id, count(*) as numlikes
FROM likes l
WHERE like_date >= unix_timestamp() - 30*24*60*60
GROUP BY user_id
ORDER BY numlikes desc
LIMIT 10;
The like_date looks like a Unix timestamp, so we should treat it as one. This gets exactly 30 days from the current time to the second. If you want to count days from midnight:
WHERE like_date >= unix_timestamp(CURDATE() - interval 30 day)
EDIT:
If it is the user_id from the post, then you would use join to get that:
SELECT p.user_id, count(*) as numlikes
FROM likes l join
posts p
on l.post_id = p.id
WHERE l.like_date >= unix_timestamp() - 30*24*60*60
GROUP BY p.user_id
ORDER BY numlikes desc
LIMIT 10;

Select distinct from the table and sort by date

I am trying to built the messaging system and I want to select distinct value from the table and sort by date and also return the read and unread status. My table structure is as id, from_user_id, to_user_id, message, datetime, read . Suppose dummy data are as follows:
id | from_user_id | to_user_id | message | datetime | read
1 | 20 | 50 | hi | 2016-3-13 06:05:30 | 1
2 | 20 | 50 | hey | 2016-3-13 06:15:30 | 0
3 | 30 | 50 | hi | 2016-3-14 06:05:30 | 0
Now I want to select distinct from_user_id and sort by date so that I can display the name of the user who is chating recently on top of list and also return the read status 1, if any of the rows with from_user_id has read status 0, then I want to return read as 0 so that I can differentiate whose user message has unread message.
Suppose, from_user_id has read status as 0 in one rows then I want to return read status as 0 when returning distinct value of from_user_id. How can I do it. I tried as follows but won't work in my case. It returns distinct value but not sorted by date and also read status in not returning what I expect.
select distinct(`from_user_id`),register.name FROM message INNER JOIN register ON register.id = message.from_user_id where message.to_user_id = $user_id GROUP BY message.id ORDER BY MAX(datetime) ASC
Try the following query:
SELECT
message.from_user_id,
register.name,
message.read
FROM message
INNER JOIN
(
SELECT
from_user_id,
MAX(datetime) max_datetime
FROM message
WHERE to_user_id = 50
GROUP BY from_user_id ) t
ON t.from_user_id = message.from_user_id AND t.max_datetime = message.datetime
INNER JOIN register ON register.id = message.from_user_id
WHERE message.to_user_id = 50
ORDER BY message.datetime ASC
UPDATED SQL FIDDLE
Sample Input:
id | from_user_id | to_user_id | message | datetime | read
1 | 20 | 50 | hi | 2016-3-13 06:05:30 | 1
2 | 20 | 50 | hey | 2016-3-13 06:15:30 | 0
3 | 30 | 50 | hi | 2016-3-14 06:05:30 | 0
Output:
from_user_id name read
20 User20 0
30 User30 0
Note: This query might give you multiple row for the same from_user_id if there exists multiple entries in your message table having the same datetime and same to_user_id.
checkout this query :
SELECT DISTINCT(form_user_id),MIN(read) FROM table_name ORDER BY datetime ASC

How can make sort by votes "by date", "by year"..?

What I am trying to implement is similar to what we have on SO. I want to rank posts by upvotes in last day, last month etc. My schema makes up two tables,
post(id, post, posted_on..)
vote(post_id, vote_value, date)
I hope the schema is pretty self explanatory. The problem being, if I sort "by day" by making a inner join on posts and vote and having a where clause('votes.date >= DATE_SUB(CURDATE(), INTERVAL 1 DAY'), it does work as intended but fails to show the other posts. I mean the posts which haven't had vote in last day are completely ignored. What I want is that those posts be given low priority but do show up in the query.
While, I may think of using union operation but i was looking for another approach.
Update: Lets say, there are two posts, 1,2.
and votes table is like,
post_id vote_value date
1 1 2012-12-19
2 1 2012-12-10
If I query, as per my approach, then only the post - "1" will show up since I have put a date constraint but I want both to show up. Here is my query:
SELECT `id`, SUM(`votes`.`votes`) AS likes_t, `post`.* FROM `posts` JOIN `votes` ON (`id` = `votes`.`post_id`) WHERE `votes`.`date` >= DATE_SUB(CURDATE(), INTERVAL 2 DAY)
If you want to show all posts, but only count the recent votes, this should do it:
SELECT `id`,
SUM(IF(`votes`.`date` >= DATE_SUB(CURDATE(), INTERVAL 2 DAY, `votes`.`votes`, 0)) AS likes_t,
`post`.*
FROM `posts` JOIN `votes` ON (`id` = `votes`.`post_id`)
If I got it right:
SELECT *, IF(vote.date>=DATE_SUB(CURDATE(), INTERVAL 1 DAY), 1, 0) as rate FROM post INNER JOIN vote ON (post.id=vote.post_id) ORDER BY rate DESC;
+------+--------+---------+------+---------------------+------+
| id | post | post_id | vote | date | rate |
+------+--------+---------+------+---------------------+------+
| 1 | first | 1 | 1 | 2012-12-19 00:00:00 | 1 |
| 1 | first | 1 | 1 | 2012-12-13 00:00:00 | 0 |
| 2 | second | 2 | 1 | 2012-12-10 00:00:00 | 0 |
+------+--------+---------+------+---------------------+------+

JOIN 2 Tables with different columns

I Have 2 Tables, One For New Pictures and One For New Users, i want to create like a wall that mixes the latest actions so it'll show new users & pictures ordered by date.
What i want is a single query and how to know inside the loop that the current entry is a photo or user.
TABLE: users
Columns: id,username,fullname,country,date
TABLE: photos
Columns: id,picurl,author,date
Desired Output:
Daniel from California Has just registred 5mins ago
New Picture By David ( click to view ) 15mins ago
And so on...
I'm begging you to not just give me the query syntax, i'm not pro and can't figure out how to deal with that inside the loop ( i only know how to fetch regular sql queries )
Thanks
You could use an union:
SELECT concat(username, " from ", country, " has just registered") txt, date FROM users
UNION
SELECT concat("New picture By ", username, " (click to view)") txt, date FROM photos INNER JOIN users ON author=users.id
ORDER BY date DESC
LIMIT 10
This assumes that author column in photos corresponds to the users table id. If author actually is a string containing the user name (which is a bad design), you'll have to do this instead:
SELECT concat(username, " from ", country, " has just registered") txt, date FROM users
UNION
SELECT concat("New picture By ", author, " (click to view)") txt, date FROM photos
ORDER BY date DESC
LIMIT 10
Make sure you have an index on date in both tables, or this will be very inefficient.
I've put together this little example for you to look at - you might find it helpful.
Full script can be found here : http://pastie.org/1279954
So it starts with 3 simple tables countries, users and user_photos.
Tables
Note: i've only included the minimum number of columns for this demo to work !
drop table if exists countries;
create table countries
(
country_id tinyint unsigned not null auto_increment primary key,
iso_code varchar(3) unique not null,
name varchar(255) unique not null
)
engine=innodb;
drop table if exists users;
create table users
(
user_id int unsigned not null auto_increment primary key,
country_id tinyint unsigned not null,
username varbinary(32) unique not null
-- all other detail omitted
)
engine=innodb;
drop table if exists user_photos;
create table user_photos
(
photo_id int unsigned not null auto_increment primary key,
user_id int unsigned not null,
-- all other detail omitted
key (user_id)
)
engine=innodb;
The important thing to note is that the primary keys of users and photos are unsigned integers and auto_increment (1,2,3..n) so I can find the latest 10 users and 10 photos by ordering by their primary keys (PK) descending and add a limit clause to restrict the number of rows returned.
-- change limit to increase rows returned
select * from users order by user_id desc limit 2;
select * from user_photos order by photo_id desc limit 2;
Test Data
insert into countries (iso_code, name) values ('GB','Great Britain'),('US','United States'),('DE','Germany');
insert into users (username, country_id) values ('f00',1),('bar',2),('stack',1),('overflow',3);
insert into user_photos (user_id) values (1),(1),(2),(3),(1),(4),(2),(1),(4),(2),(1);
So now we need a convenient way (single call) of selecting the latest 10 users and photos. The two tables are completely different so a union isnt going to be the best approach so what we'll do instead is write a stored procedure that returns two resultsets and handle generating the wall (merge resultsets) in our php script.
Stored procedure
Just a wrapper around some SQL code - think of it like SQL's version of a function call
drop procedure if exists list_latest_users_and_photos;
delimiter #
create procedure list_latest_users_and_photos()
begin
-- last 10 users
select
'U' as type_id, -- integer might be better
u.user_id,
u.country_id,
u.username,
-- other user columns...
c.name as country_name
from
users u
inner join countries c on u.country_id = c.country_id
order by
u.user_id desc limit 10;
-- last 10 photos
select
'P' as type_id,
up.photo_id,
up.user_id,
-- other photo columns...
u.username
-- other user columns...
from
user_photos up
inner join users u on up.user_id = u.user_id
order by
up.photo_id desc limit 10;
end #
delimiter ;
Testing
To test our stored procedure all we need to do is call it and look at the results.
mysql> call list_latest_users_and_photos();
+---------+---------+------------+----------+---------------+
| type_id | user_id | country_id | username | country_name |
+---------+---------+------------+----------+---------------+
| U | 4 | 3 | overflow | Germany |
| U | 3 | 1 | stack | Great Britain |
| U | 2 | 2 | bar | United States |
| U | 1 | 1 | f00 | Great Britain |
+---------+---------+------------+----------+---------------+
4 rows in set (0.00 sec)
+---------+----------+---------+----------+
| type_id | photo_id | user_id | username |
+---------+----------+---------+----------+
| P | 11 | 1 | f00 |
| P | 10 | 2 | bar |
| P | 9 | 4 | overflow |
| P | 8 | 1 | f00 |
| P | 7 | 2 | bar |
| P | 6 | 4 | overflow |
| P | 5 | 1 | f00 |
| P | 4 | 3 | stack |
| P | 3 | 2 | bar |
| P | 2 | 1 | f00 |
+---------+----------+---------+----------+
10 rows in set (0.01 sec)
Query OK, 0 rows affected (0.01 sec)
Now we know that works we can call it from php and generate the wall.
PHP Script
<?php
$conn = new Mysqli("localhost", "foo_dbo", "pass", "foo_db");
$result = $conn->query("call list_latest_users_and_photos()");
$users = array();
while($row = $result->fetch_assoc()) $users[] = $row;
$conn->next_result();
$result = $conn->use_result();
$photos = array();
while($row = $result->fetch_assoc()) $photos[] = $row;
$result->close();
$conn->close();
$wall = array_merge($users, $photos);
echo "<pre>", print_r($wall), "</pre>";
?>
Hope you find some of this helpful :)

Categories