SQL - Use Value of Column Name as Paramter - mysql

I have a SQL query to get the amount of 'likes' a user has. There are two different tables, one table holds different user data (not the amount of likes). The other table holds the like logs such as who gave the like, who's receiving the like, when it was liked etc. I'm create a top ten list of the most liked users.
What I'm wondering is if it's possible to use the current value of the column as a parameter while the query is being run, like so:
SELECT `username` FROM `users` ORDER BY (SELECT COUNT(*) FROM `likes` WHERE `liked_user`=?) LIMIT 10
Where the question mark would be replaced with something that represents the current value of username (I assume SQL has to do some kind of loop to get its results)
The alternative I have is having a column in the user table holding the amount of likes, then sorting the based on that, but I was wondering if a query could perform the same job by looking at the like logs.

Yes, you can do that. Should be as easy as:
SELECT u.`username`
FROM `users` u
ORDER BY (SELECT COUNT(*)
FROM `likes` l
WHERE l.`liked_user` = u.`username`)
LIMIT 10
You can also do the same without subquery:
SELECT u.`username`
FROM `users` u
JOIN `likes` l ON l.`liked_user` = u.`username`
GROUP BY u.`username`
ORDER BY COUNT(*)

Related

mysql- Display a list of users sorted according to the time

I have a page wherein I would like to display the list of user who have signed up for the paid subscription.
The list should be sorted according to the subscribed_time row present in another table called paid_subs.
This is the query I am using
SELECT *
FROM user
WHERE EXISTS
( SELECT *
FROM paid_subs
WHERE paid_subs.user_id= user.id
ORDER
BY paid_subs.subscribed_time DESC)
What the above query is doing is simply giving me the list of users who have a paid subscription.
I would like the query to display the list of paid subscriber in a subscribed_time sorted order.
You need to do a JOIN between the two tables on user_id. Inner Join will ensure that only those users are considered, which have paid for the subscription.
Now, do a simple Order By to sort the data based on subscription time.
Also, you should fetch only specific fields (as per your application code requirements), instead of using wildcard (*). Do read: Why is SELECT * considered harmful?
Use the following query:
SELECT u.* -- please change this to specific fields, eg: u.id, u.name etc
FROM user AS u
INNER JOIN paid_subs ps ON ps.user_id = u.id
ORDER BY ps.subscribed_time DESC
Exists simply check if the corresponding records exists and returns a true or false result. If you want to order by a field from another table, then you must use a join.
SELECT * FROM user
INNER JOIN paid_subs ON paid_subs.user_id= user.id
ORDER BY paid_subs.subscribed_time DESC

How can I filter out results based on another table. (A reverse join I guess?)

Basically, I have a table which contains two fields: [id, other] which have user tokens stored in them. The goal of my query is to select a random user that has not been selected before. Once the user is selected it is stored in the table shown above. So if Jack selects Jim randomly, Jack cannot select Jim again, and on the flip side, Jim cannot select Jack.
Something like this is what comes to mind:
SELECT * FROM users
WHERE (SELECT * FROM selected WHERE (id=? AND other=?) OR (id=? AND other=?));
Well, first of all I've read that uses sub-queries like this is extremely inneficient, and I'm not even sure if I used the correct syntax, the problem is however, that I have numerous tables in my scenario which I need to filter by, so it would look more like this.
SELECT * FROM users u
WHERE (SELECT * FROM selected WHERE (id=? AND other=?) OR (id=? AND other=?))
AND (SELECT * FROM other_table WHERE (id=? AND other=?) OR (id=? AND other=?))
AND (SELECT * FROM diff_table WHERE (id=? AND value=?))
AND u.type = 'BASIC'
LIMIT = 1
I feel like there's a much, much more efficient way of handling this.
Please note: I don't want a row returned at all if the users id is present in any of the nested queries. Returning "null" is not sufficient. The reason I have the OR clause is because the user's id can be stored in either the id or the other field, so we need to check both.
I am using Postgre 9.5.3, but I added the MySQL tag as the code is mostly backwards comptable, Fancy Postgre only solutions are accepted(if any)
You can left join to another table, which produces nulls where no record is found:
Select u.* from users u
left selected s on s.id = u.id or s.other = u.other
where s.id is null
The or in a join is different, but should work. Example is kinda silly...but as long as you understand the logic. Left join first table to second table, where second table column is not null means there was atleast one record found that matched the join conditions. Where second table column is null means no record was found.
And you are right...avoid the where field = (select statement) logic when you can, poor performer there.
Use an outer join filtered on missed joins:
SELECT * FROM users u
LEFT JOIN selected s on u.id in (s.id, s.other) and ? in (s.id, s.other)
WHERE u.id != ?
AND s.id IN NULL
LIMIT 1

A bit of trouble with mySQL querying

say I have a table called "users", which houses all the users of a system. Each user has a "role" as defined by the "roles" column in addition to a "name" and "id" column. Each user with the role "worker" is advised by a "supervisor" as defined by the "advised_by" column.
I'd like to query my database so that it returns the both the name of the worker and their supervisor and one line, is that possible without joins?
Thanks for any advise, I've been banging my head against this for a while now
Edit: Thanks to everybody who tried to help. Sorry, I guess that was a pretty vague/awful description in retrospect. I'll try to model the table better here.
Columns: Name, ID, Role, Advised_by
Row 1: Bob, 123, Worker, 321
Row 2: Tom, 321, Supervisor, N/A
I would like to return a result with all the people tagged as "Workers", along with their name and the name of their supervisor, so something like: Bob, Worker, Tom on one line.
I was trying to avoid adding a join because my professor asked us not to use them due to the performance hit, but it doesn't seem like there's a practical way around it, in which case I'll be alright with a join.
Edit 2: Guess I'm just an idiot, I realized after I typed it all out I could just do it pretty easily with aliasing. Sorry for the trouble guys
Some more details would be necessary, but you need to join the table to get both the name of the supervisor and worker in one MySQL sentence.
Assuming the advised_by column contains the ID of the user who advises this worker:
SELECT u.id, u.name, u2.id AS supervisorID, u2.name AS supervisorNAME
FROM users u, users u2
WHERE u.advised_by = u2.id AND u.role = 'worker'
At the risk of repeating the answer provided by EpochGalaxy...
Q: Is that possible without joins?
A: Yes. It is possible. One approach is to use a correlated subquery.
Consider the query you need to run to get the supervisor name...
SELECT s.name
FROM users s
WHERE s.id = 321
What if you ran a query like that for each row you retrieved from users.
Say you ran query like this:
SELECT u.id
, u.name
, u.role
, u.advised_by
FROM users u
ORDER BY u.id
And for each row from that query, right before you returned it, you ran a query to get the name of the supervisor.
We can achieve that by using a subquery in the SELECT list.
As a trivial example of running a subquery in the SELECT list consider this query. (There's no logical reason we'd run a query like this, but as a simple demonstration that we can run a subquery...
SELECT u.id
, u.name
, u.role
, u.advised_by
, ( SELECT 'bar' ) AS foo
FROM users u
ORDER BY u.id
The rows returned by this query will include an additional column, named foo. The value assigned to the column will be provided from the result of the subquery... "SELECT 'bar'".
Now that we know we can run a subquery in the SELECT list, and return a column with that, we can try something else. Like this:
SELECT u.id
, u.name
, u.role
, u.advised_by
, ( SELECT s.name
FROM users s
WHERE s.id = 321
) AS s_name
FROM users u
ORDER BY u.id
With that, for each row returned by the outer query, MySQL will run the subquery in the SELECT list, and take the value returned by the subquery, and put the value returned into a column of the row being returned by the outer query.
That subquery is going to get only the name from the row in users with id = 321.
So that doesn't really get us what we need.
Here's the trick... instead of a literal value 321, we can put there an expression that includes a reference to the table in the outer query.
SELECT u.id
, u.name
, u.role
, u.advised_by
, ( SELECT s.name
FROM users s
WHERE s.id = u.advised_by
) AS s_name
FROM users u
ORDER BY u.id
Aha!
Now, for each row returned by the outer query, MySQL will run the subquery in the SELECT this.
This time, the subquery includes a reference to the advised_by column from the row of the outer query. MySQL will take the value that's in the advised_by column from the row being returned (by the outer query), and then slide that into the subquery before the subquery executes. The return from the subquery goes into the column of the resulset.
With this, the results we get from the sbuquery are determined by values in the outer query. The subquery is related to the outer query.
Some important restrictions to notes: The subquery in a SELECT list of another query must return exactly one column. (It can be an expression, a literal. But the return from the subquery is not allowed to return two or more columns.
The subquery must return no more than one row. If it returns zero rows, a NULL value is assigned to the column in the outer query.
The subquery in the example above satisfies the conditions. There's exactly one expression in the SELECT list, and the id column is unique in the users table, so it can't return more than one row.
The SQL terminology for using a subquery like this is correlated subquery.
So, to answer your question, yes... it is possible to achieve the specified result without a join operation.
Some other notes:
In terms of performance, it's important that the subquery be efficient. It is going to be executed for every row returned by the outer query. If we're returning 10,000 rows, that's 10,000 executions of the subquery.
So, this approach is best suited to cases where the outer query is returning a small number of rows, and the correlated subquery is very efficient.
Use the following query:
SELECT (u.name)workername, (select name from users where id=u.advised_by)supervisor FROM `users` u where u.roles="worker"
Sample table structure as described by you:
--
-- Database: `test`
--
-- --------------------------------------------------------
--
-- Table structure for table `users`
--
CREATE TABLE `users` (
`id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`roles` varchar(255) NOT NULL,
`advised_by` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `users`
--
INSERT INTO `users` (`id`, `name`, `roles`, `advised_by`) VALUES
(1, 'test1', 'supervisor', 0),
(2, 'test2', 'worker', 1),
(3, 'test3', 'worker', 1);

Can you build a MySQL query to show not found results from the conditional

I am trying to find out how to find the emails that do not exist in a table using the emails from the conditional.
I could create a table with these emails but that seems like overkill for what I need it for.
What I am looking for is a query that would show me the conditional value and NULL as the user ID.
Is this possible?
I have a query like this:
SELECT u.uid, u.mail
FROM `users` u
WHERE u.mail IN (
'alot#of',
'emails#that',
'ineed#tofind',
)
This works great at finding the emails and associating the user id. Now I need to identify which emails do not exist in the result. I am currently only using 56 emails and 6 do not appear in the list. I am trying to identify which emails are not found.
NOT IN won't work as I have over 40,000 users. I only want to identify the emails not found from my conditional. I have 56 emails and only 50 results. I need to identify the 6 not found (they may not even be in the table at all)
Let me attempt to clarify this a little more:
I am given a list of emails for supposed accounts in the system. I am trying to find the accounts from the given email. This part is fine. Now, the issue I am having, I was given 56 emails but only 50 were found. I need to identify which emails out of the 56 were not found. The emails are all thrown into the conditional. NOT IN won't work because it would return all user but the 50 that were found. (roughly 40,000) I just need to identify the emails from the conditional that were not found in the table.
Thanks for any insight or suggestions to do what I need.
There isn't a way to do what you want without creating some additional items to track the emails. Basically, you're trying to get MySQL to tell you which items in the WHERE portion aren't found, but MySQL can only tell you about rows in a table.
You need to make a secondary table that stores the email addresses from your list, call it list. I would make it a single column table with just the emails. Then LEFT JOIN it against the users table and find where the uid is null.
SELECT u.uid, l.mail
FROM `list` l
LEFT JOIN `users` u ON u.mail=l.mail
WHERE u.uid IS NULL
As posted in the comments, NOT IN may be helpful. But there are also other ways. One of them is to left join your table with the result of your query and show only non-coincident rows:
select u.uid, u.mail
from users as u
left join (
select u.uid, u.mail
from users
where mail in ('alot#of','emails#that','ineed#tofind')
) as a on u.uid = a.uid
where a.uid is null;
Add the fields you need to the join (if uid is not enough)
So your question now becomes more complicated... you want to find all the E-Mails in your condition that are not found in your table.
As far as I know, there's not a simple SQL sentence that will give you that... but you can work with temp tables and get it. The solution implies:
Create a temporary table to hold the values you want to search (and add the appropriate indexes to it)
Insert the values you want to search
Execute a select query to find non-matching rows
So... let's do it:
-- 1. Create a temp table to hold the values
drop table if exists temp_search_values;
create temporary table temp_search_values (
mail varchar(100),
unique index idx_mail(mail) -- Don't allow duplicate values here
);
-- 2. Insert the search values
insert into temp_search_values (mail) values
('alot#of'),('emails#that'),('ineed#tofind');
-- 3. Execute the query
select a.*
from users as u
left join temp_search_values as a on u.mail = a.mail
where u.mail is null;
Remember: Temporary tables are only visible to the connection that created them, and are deleted when the connection is closed or killed.
NULL is a strange result. It's not true and it's not false. If you want to check for it, you have to look specifically.
SELECT u.uid, u.mail
FROM `users` u
WHERE u.mail NOT IN (
'alot#of',
'emails#that',
'ineed#tofind',
) and u.uid IS NULL
* Oh, I see what you're getting at. This will work, although it's not pretty. *
select * from
(SELECT 'emails#that' as v
UNION SELECT 'alot#of' as v,
UNION SELECT 'ineed#tofind' as v
) as test
left join users on u.mail = test.v
where u.uid is null

How to select distinct rows from a table without a primary key

I need to show a Notification on user login if there is any unread messages. So if multiple users send (5 messages each) while the user is in offline these messages should be shown on login. Means have to show the last messages from each user.
I use joining to find records.
In this scenario Message from User is not a primary key.
This is my query
SELECT
UserMessageConversations.MessageFrom, UserMessageConversations.MessageFromUserName,
UserMessages.MessageTo, UserMessageConversations.IsGroupChat,
UserMessageConversations.IsLocationChat,
UserMessageConversations.Message, UserMessages.UserGroupID,UserMessages.LocationID
FROM
UserMessageConversations
LEFT OUTER JOIN
UserMessages ON UserMessageConversations.UserMessageID = UserMessages.UserMessageID
WHERE
UserMessageConversations.MessageTo = 743
AND UserMessageConversations.ReadFlag = 0
This is the output obtained from above query.
MessageFrom -582 appears twice. I need only one record of this user.
How is it possible
I'm not entirely sure I totally understand your question - but one approach would be to use a CTE (Common Table Expression).
With this CTE, you can partition your data by some criteria - i.e. your MessageFrom - and have SQL Server number all your rows starting at 1 for each of those partitions, ordered by some other criteria - this is the point that's entirely unclear from your question, whether you even care what the rows for each MessageFrom number are sorted on (do you have some kind of a MessageDate or something that you could order by?) ...
So try something like this:
;WITH PartitionedMessages AS
(
SELECT
umc.MessageFrom, umc.MessageFromUserName,
um.MessageTo, umc.IsGroupChat,
umc.IsLocationChat,
umc.Message, um.UserGroupID, um.LocationID ,
ROW_NUMBER() OVER(PARTITION BY umc.MessageFrom
ORDER BY MessageDate DESC) AS 'RowNum' <=== totally unclear yet
FROM
dbo.UserMessageConversations umc
LEFT OUTER JOIN
dbo.UserMessages um ON umc.UserMessageID = um.UserMessageID
WHERE
umc.MessageTo = 743
AND umc.ReadFlag = 0
)
SELECT
MessageFrom, MessageFromUserName, MessageTo,
IsGroupChat, IsLocationChat,
Message, UserGroupID, LocationID
FROM
PartitionedMessages
WHERE
RowNum = 1
Here, I am selecting only the "first" entry for each "partition" (i.e. for each MessageFrom) - ordered by a "imagined" MessageDate column so that the most recent (the newest) message would be selected.
Does that approach what you're looking for??
If you think of them as same rows, I assume you don't care about the message field.
In this case you can use the DISTINCT clause:
SELECT DISTINCT
UserMessageConversations.MessageFrom, UserMessageConversations.MessageFromUserName,
UserMessages.MessageTo, UserMessageConversations.IsGroupChat,
UserMessageConversations.IsLocationChat,
UserMessages.UserGroupID,UserMessages.LocationID
FROM
UserMessageConversations
LEFT OUTER JOIN
UserMessages ON UserMessageConversations.UserMessageID = UserMessages.UserMessageID
WHERE
UserMessageConversations.MessageTo = 743
AND UserMessageConversations.ReadFlag = 0
In general with distinct clause you have a row for every distinct group of row attributes.
If your requirement instead is to show a single field for all the messages (example: every message folded in a single message with a separator between them) you can use an aggregate function, but in SQL Server it seems is not that easy.