I've been on this for a few hours without solution. I thought of laravel relationships but don't know how to pass a second condition because I need to relate with 3 tables. I'd like to use the query below in laravel.
SELECT
subscriptions.subscribed_to,
broadcasts. *,
FROM subscriptions
INNER JOIN broadcasts
WHERE subscriptions.subscriber = {$user_id}
AND (
SELECT COUNT(*) FROM seen_broadcasts
WHERE user_id = {$user_id}
AND broadcast_id = broadcasts.id
) = 0
ORDER BY broadcast.date DESC
There are 3 tables.
subscriptions: subscriber_id subscribes to broadcaster_id.
broadcasts: where broadcaster's message is saved.
seen_broadcast: where the information of subscribers are saved when they read a broadcast. This helps us provide detailed stats to broadcaster. user_id = subscriber_user_id, broadcast_id = broadcast_message_id
I want to be able to get broadcasts from all broadcaster that userA has subscribed to and have not seen.
The query above currently works outside laravel.
After much runarounds, I ended up with this:
$broadcast_result = DB::select( DB::raw("
SELECT
subscriptions.subscribed_to,
broadcasts.*
FROM subscriptions
INNER JOIN broadcasts
WHERE subscriptions.browser_agent_id = :subsc_id
AND broadcasts.user_id = subscriptions.subscribed_to
AND (
SELECT COUNT(*) FROM broadcasts_seen
WHERE broadcast_id = broadcasts.id
AND subscriber_id = subscriptions.subscriber_id
) = 0
ORDER BY broadcasts.date DESC LIMIT 1
"), array(
'subsc_id' => $subscriber->id
));
$broadcast_set = $broadcast_result[0];
Also add use DB; in the controller.
If there's a better way to do this, please share.
Related
I am getting data from a table and ordering them by a column in another table. Below is my query.
$query = "SELECT Chatroom.ChatID, Chatroom.User1, Chatroom.User2 FROM `Chatroom` JOIN `Messages` ON Chatroom.ChatID = Messages.ChatID WHERE User1 = ? OR User2 = ? ORDER BY Messages.MessageDate";
Say there is a chat between two people and they've messaged 10 times. I will get 10 results when really I want 1 result. I realize it's looping through messages because I joined them on chatID but I do that to get the dates.
Is there a better way to get this or is there any easy adjustment to my query that will work?
If you're using MySQL, maybe try this?
SELECT c.ChatID, c.User1, c.User2
FROM Chatroom AS c
JOIN (
SELECT mx.*
FROM Messages AS mx
WHERE mx.ChatID = c.ChatID
ORDER BY mx.MessageDate
LIMIT 1
) AS m ON m.ChatID = c.ChatID
WHERE c.User1 = ?
OR c.User2 = ?
ORDER BY m.MessageDate;
Try this instead?
SELECT c.ChatID, c.User1, c.User2
FROM Messages AS m
JOIN Chatroom AS c on c.ChatID = m.ChatID
WHERE (c.User1 = ? OR c.User2 = ?)
GROUP BY c.ChatID
ORDER BY m.MessageDate
Basically what i am trying to do is to suggest people based on common interests.
I have a table of Users.
I have a table of Interested_People where UserID + InterestID is stored.
I have a table of Contactlist where people who are added with each other is stored.
What I want is to only output people who are not your friends.
I searched a lot in internet but couldn't find something like so.
Although I created a query but it is very slow. Now I Kindly request you guys if you can edit my query a bit and make it much more bandwidth & time efficient.
SELECT *
FROM users
WHERE id IN(SELECT userid
FROM interested_people
WHERE interested_in IN(SELECT interested_in
FROM interested_people
WHERE userid = [userid])
AND id NOT IN(SELECT user1 AS my_friends_userid
FROM contactlist f
WHERE f.user2 = [userid]
AND accepted = 1
UNION
SELECT user2 AS my_friends_userid
FROM contactlist f
WHERE f.user1 = [userid]
AND accepted = 1))
AND id != [userid]
ORDER BY Rand ()
LIMIT 0, 10;
This query actually does the job but it takes very long about 16 sec in my local machine. and that's not what I want. I want a fast and reliable one.
Thanks in advance!
Subqueries in WHERE clauses are often slow in MySQL; at least slower than comparable JOINs.
SELECT others.*
FROM interested_people AS userI
INNER JOIN interested_people AS othersI
ON userI.interestid = othersI.interestid
AND userI.userid <> othersI.userid
INNER JOIN users AS others ON othersI.user_id = others.userid
LEFT JOIN contactlist AS cl
ON userI.userid = cl.user1
AND others.userid = cl.user2
AND cl.accepted = 1
WHERE userI.userid = [userid]
AND cl.accepted IS NULL
ORDER BY RAND()
LIMIT 0, 10;
Note: intuition makes me wonder if contactlist might be better as a where subquery.
The AND cl.accepted IS NULL ends up processed after the JOINs, resulting in allowing only results that did NOT have a match in contactlist.
If you want to enhance things a bit further:
SELECT others.*, COUNT(1) AS interestsCount
...
GROUP BY others.userid
ORDER BY interestsCount DESC, RAND()
LIMIT 0,10;
This would give you a random selection of the people that share the most interests in common.
First, looking at your interested-in query and assuming the "userID"
you are testing with is = 1. Sounds like you are trying to get one level
away from those user 1 is also interested in...
SELECT userid FROM interested_people
WHERE interested_in IN
( SELECT interested_in FROM interested_people
WHERE userid = [userid] )
Sample Data for Interested_People
userID Interested_In
1 5
1 7
1 8
2 3
2 5
2 7
7 1
7 2
7 5
8 3
In this case, the innermost returns interested_in values of 5, 7, 8.
Then, getting all users who are interested in 5, 7 and 8 would return 2 and 7.
(but since both users 2 and 7 are interested in 5, the 2 ID would be returned TWICE
thus a possible duplicate join later on. I would do distinct. This same
result could be done with the following query which you could sample times with...
SELECT distinct ip2.userid
from
interested_people ip
join interested_people ip2
ON ip.interested_in = ip2.interested_in
where
userid = [parmUserID]
Now, you need to exclude from this list all your contacts already accepted.
You could then left-join TWO TIMES for the from/to contact and ensure NULL
indicating not one of the contacts... Then join again to user table to
get the user details.
SELECT
u.*
from
users u
JOIN
( SELECT distinct
ip2.userid
from
interested_people ip
join interested_people ip2
ON ip.interested_in = ip2.interested_in
left join contactList cl1
ON ip2.userid = cl1.user1
AND cl1.accepted = 1
left join contactList cl2
ON ip2.userid = cl2.user2
AND cl2.accepted = 1
where
ip.userid = [parmUserID]
AND NOT ip2.userID = [parmUserID] ) PreQuery
ON u.id = PreQuery.userID
order by
RAND()
limit
0, 10
I would have two indexes on your contactList table to optimize both left-joins... with user1 and user2 in primary position... Similarly for the interested_people table.
table index
contactList ( user1, accepted )
contactList ( user2, accepted )
interested_people ( userid, interested_in )
interested_people ( interested_in, userid )
I would expect your user table is already indexed on the ID as primary key.
I think this will give you the same results but perform a lot better:
SELECT * FROM Users u
INNER JOIN interested_people i
ON u.id = i.userid
WHERE NOT EXISTS
(SELECT * FROM contacts WHERE user1 = [userid] or user2 = [userid] and accepted=1)
AND id != [userid]
ORDER BY Rand()
LIMIT 0, 10
Skip the ORDER BY clause if that is at all reasonable. That will be the most expensive part
The select and join clauses give you the users who are interested in connecting and the WHERE NOT EXISTS is a performant way to exclude those contacts already listed.
So yesterday, I am trying to sort data in groups made by Group by
I must select which data I want to show in those group
There is list of debts and each person may be in debt in the past but never have more than 1 unpaid debt
I need to know how many times how many times each user have been in debt before this last debt.
This is the column in the data base
Table "Users"
uid | name | date_of_birth
Table "Debt"
uid | debt_duration | paid_count | created_date
I end up with a hack like this in php
$res = mysql_query( "
SELECT * FROM Debt
JOIN Users
WHERE Users.uid = Debt.uid
ORDER BY created_date
GROUP BY Debt.uid");
while( $row = mysql_fetch_array( $res ) ){
$uid = $row['uid'];
$r = mysql_fetch_array( mysql_query("SELECT COUNT(*) FROM Debt WHERE uid = $uid") );
$previous_debts_count = $r[0];
}
This script is quite heavy but fortunately my client doesn't complain.
The script run at around 3 seconds top
But I need to know better ways to do this
sorry for the strange formatting, I am new here ...
I think the query you want is this:
SELECT Users.uid, COUNT(*) as cnt
FROM Debt JOIN
Users
ON Users.uid = Debt.uid
GROUP BY Debt.uid
ORDER BY created_date ;
Just loop through the results and don't use multiple queries for this. Check that Users.uid is the primary key on the users table. And add an index on debt(uid) to improve performance.
First of all, you should never use * in an SQL statement.
It makes the query highly vulnerable to SQL injection.
And I recommend you to use a PDO or a PHP framework.
Try this:
SELECT COUNT(Debt.uid) AS users
FROM Debt
LEFT JOIN Users
ON Users.uid = Debt.uid
GROUP BY Debt.uid
Following are the tables:
Table User:
User_Id = 1,2,3;
User_Name: A,B,C;
Table Business:
Business_Id = 1,2,3;
Business_Name: A,B,C;
Business_Detail: Details_A, Details_b, Details_c;
Table Visits:
Visit_Id = 1,2,3,4,5,6:
User_Id = 1,1,1,2,1,1;
Business_Id = 1,1,1,2,2,3;
I need to create a function that return a list of the visits and the information about the business that the user visited. So far I have got the list of the store the user has visited but don't know where to go from there.
function visit_count($user_id=1){
global $database;
$sql = "SELECT * FROM visits WHERE user_id ='{$user_id}' LIMIT 0 , 30";
$result_set = $database->query($sql);
$visits = mysql_fetch_array($result_set);
//Get the unique ids of the business
//Run another query that has the business information
//combing both queries.
}
Thanks for the quick response guys. It's pretty much what I am looking for I think I am looking for the query to return an object as following:
Object:
- Business:
- Business_id;
- Business_name;
- Visit_counts;
- Business:
- Business_id;
- Business_name;
- Visit_counts;
So basically the object will have the business information and the no of times that the user has visited the store.
Thanks much for all the help
Well, first off you should look into prepared statements, it is the current best practice for working with sql in PHP.
What it sounds like you need are joins.
$sql = "SELECT * FROM visits v
JOIN business b ON b.Business_Id = v.Business_Id
JOIN user u ON u.Business_Id = v.Business_Id
WHERE v.user_id ='{$user_id}' LIMIT 0 , 30";
SELECT Visits.* , Business.Business_Name, Business.Business_Details
FROM Visits
LEFT JOIN Business on Business.Business_Id = Visits.Business_Id
WHERE Visits.User_Id =1
try this SQL for each user
Try following SQL code:
SELECT business.business_id, business.business_name, COUNT(business.business_id) AS visit_counts
FROM business
LEFT JOIN visits
ON business.business_id = visits.business_id
GROUP BY business_id, user_id
Result:
business_id |business_name |visit_counts
1 A 3
2 B 1
2 B 1
3 C 1
You need Joins:
SELECT v.*, b.Business_Name, b.Business_Detail FROM visits as v
JOIN Business as b on b.Business_Id = v.Business_Id
WHERE v.user_id ='{$user_id}' LIMIT 0 , 30
Also - use mysqli and ensure you are sanitizing your inputs!
Edit
Follow #KHMKShore's advice on using prepared statements.
I have three tables.
I combine the company and component tables with this code
$questions_query = "SELECT company_mast.id, component_mast.component_id
FROM company_mast
LEFT JOIN component_mast
ON company_mast.id = component_mast.company_id
WHERE component_mast.component_name = '".$component_name."'
AND company_mast.company_name = '".$company_name."'";
The result is as desired, If I put company_name as Bells and component_name as Assets I get and id of 3 for Bells and an id of 9 for Assets. Now if you look at the customfields table I need to pull all the questions with the a specific company_id and component_id.
Example: If the user enters Bells and Assets they need to receive all questions with the company_id of 3 and the component_id of 9.
So this is my query
SELECT *
FROM customfield_mast
LEFT JOIN ( SELECT company_mast.id, component_mast.component_id
FROM company_mast
LEFT JOIN component_mast
ON company_mast.id = component_mast.company_id
WHERE component_mast.component_name = 'Assets'
AND company_mast.company_name = 'Bells')
att
ON customfield_mast.company_id = customfield_mast.component_id
This however returns all questions in my db, which is not what I want. I'm positive my last "ON" statement is the problem, however I don't know what the correct statement would be. I have not started with SQL injection protection, this is grass roots to get my queries to work. Thanks for the help
What's wrong with another join?
SELECT company_mast.id, component_mast.component_id, CFM.DisplayName
FROM company_mast
LEFT JOIN component_mast
ON company_mast.id = component_mast.company_id
LEFT JOIN CustomField_mast CFM
ON CFM.Company_ID = Component_mast.Company_ID
and CFM.Component_ID = component_Mast.Component_ID
WHERE component_mast.component_name = '".$component_name."'
AND company_mast.company_name = '".$company_name."'";
SELECT * FROM `customfield_mast`
WHERE `company_id` =
(SELECT `id` FROM `company_mast` WHERE `company_name` = '$company_name')
AND `component_id` IN
(SELECT GROUP_CONCAT(`component_id`) FROM `component_mast`
WHERE `component_name` = '$component_name')