A bit of trouble with mySQL querying - mysql

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);

Related

MySQL Join statement to get data from two tables into a datagridview

I have two tables that I'm trying to join, 'holidays' and 'users'.
Users contains all my user info, the the column 'id' being primary and unique.
Holidays contains a column called 'userid', which corresponds to the id in the user table.
I'm struggling to get the join statement to work... what I'm looking for is the result of the select statement to give me the friendlyname (column 'fname' in user table) instead of giving me the value of userid.
Here's what I'm trying...
SELECT * FROM holidays JOIN users on users.id=holidays.userid WHERE holidays.status = 0
But i'm not getting a correct result - SQL executes without error, but my DGV is filled with tons of erroneous results.
Apologies If I have not used the correct terminology or whatever.
I'm new to the concept of joins.
Here is hopefully a better explanation of what I am after...
Thanks in advance.
You need to select the specific values you want from every table in the JOIN:
SELECT u.fname
FROM holidays h
JOIN users u
ON u.id = h.userid
WHERE h.status = 0
by the alias (FROM users u) you can select column from users table by u.fname
First try to right join to the User table. If you just want the fname then select the column name in the SELECT query, as SELECT * takes more time then SELECT column name.

Use count of unrelated table in SQL query

I have this query:
select skill.name, IFNULL(Round(((SUM(ROUND((student_skills.value/skill.value)*100,0)))/82),0),0) as successRate from skill left JOIN student_skills on skill.id = student_skills.skill_id group by skill.name
This query returns exactly what I want but I need to replace constant 82 (just for example) with number of rows in table user (something like COUNT(user.name)).
Problem is that user is not related to skill or student_skill table in any way.
How should I alter my query so that it would use current count of users?
Thanks
Use a subquery
select skill.name,
IFNULL(Round(((SUM(ROUND((student_skills.value/skill.value)*100,0)))/(select COUNT(*) from user)),0),0) as successRate
from skill
left JOIN student_skills on skill.id = student_skills.skill_id
group by skill.name

SQL query from 3 different tables

There are 3 tables:
Users table
------------
|uid|username|
------------
Values table
------------------
|vid|values|checked|
------------------
Relations
-----------
|cid|uid|vid|
-----------
Relations table contains user ids related to value ids. How to select value id from values table that is not related to given user id in relations table?
EDIT:
What I tried so far:
SELECT vid FROM relations where uid=user_id //this gives me array of value ids
SELECT vid FROM values where vid!=vid1 AND vid!=vid2 .....
EDIT2:
Basic solution can be found here. But is there more efficient way? If table is very large for both values table and relations table basic solution is not efficient.
Which dbms are you using? Does it support the minus clause? If yes you can do something like this
select vid from values
minus
select vid from relations where uid = #user_id
this should give the vid's which are not mapped to a given user id
Another way to do this is through a not-exists clause (handy if your dbms doesn't support the minus clause)
select v.vid from values v where not exists (select 1 from relations r where
r.vid = v.vid and r.user_id = #user_id)
I would caution against using the not in clause though. Its performance is questionable and fails if the inner query returns a null value, which though is not possible in your case, but you should make it a habit to never use the 'not in' clause with a sub-query. Only use it when you have a list of literal values e.g. '... vid not in (1, 2, 3, 4)'. Whenever you have to 'Minus' something from one table based on values in another table use the 'not exists' and never 'not in'
I think you can execute a simple query like this (assuming that the data type of user identifier is int):
DECLARE #givenUserID int --local variable where you store the given user identifier
SELECT vid
FROM Values
WHERE vid NOT IN (SELECT vid FROM Relations where uid = #givenUserID)
Is it ok for you ?
select vid from values where vid not in (select vid from relations where uid = user_id)
I think something simple like this query will suffice.
If there is no uid for a particular entry in the value table, then there shouldn't be an entry in the relations table either.
SELECT vid
FROM values
LEFT JOIN relations on values.vid = relations.vid
WHERE relations.uid IS NULL
select distinct v.vid
from values v
left join relations r on (r.vid=v.vid)
where r.uid != user_id
It's unfortunate that MySQL doesn't support with; this is not going to perform very well, unfortunately.
If a Value is exactly 0 or 1 time in your Relations table, you can use a JOIN for that:
SELECT `Values`.`vid` FROM `Values`
LEFT JOIN `Relations` ON (`Values`.`vid` = `Relations`.`vid`)
WHERE `Relations`.`uid` != 1;
This will not work if a Value is more than 1 time in the Relations table because the WHERE would match another row with a different uid in this case. It is the same with a NOT IN, this could also match a different row with the same vid but another uid.
If every Value is at least once in the Relations table, the most efficient way is to query only the Relations table:
SELECT DISTINCT `Relations`.`vid` FROM `Relations`
WHERE `Relations`.`uid` != 1;
If a Value can be 0, 1, or more times in the Relations table, the best way is to use an EXISTS (see also taimur's answer):
SELECT `Values`.`vid` FROM `Values`
WHERE NOT EXISTS (
SELECT * FROM `Relations`
WHERE `Relations`.`vid` = `Values`.`vid` AND `Relations`.`uid` = 1
);
However, EXISTS is a bit slower than the IN or JOIN, so you should compare how the execution times are in your case.

SQL - Use Value of Column Name as Paramter

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(*)

Need Help writing a MySQL query

I have a database with customer information, orders, etc. I need to run a query that returns all customers who have not placed an order at all.
Relevant tables: login and orders
Relevant Columns: login.loginID, login.loginFirstName, login.loginLastName, login.loginEmailAddress AND orders.OrderuserID
So essentially - in psuedocode: compare table login, column loginID for matches in the orders table under orders.OrderUserID. If no match exists (as in no orders placed) then output the users First Name, Last Name and Email address.
I have been racking my brain but having some real issues with the language. I'm a big time N00B when it comes to SQL.
Basically it'll look like that:
SELECT l.login_id
FROM login l
LEFT JOIN orders o
ON l.login_id = o.login_id
WHERE o.login_id IS NULL
The key is using LEFT JOIN with WHERE ... IS NULL condition. In other words, you specifically look for the rows in login table that don't have any information 'extended' within orders table.
That's just a general description, but I hope it should be helpful in your process of constructing the big query specific to your case. )
select loginFirstName, loginLastName, loginEmailAddress
from login
where loginID not in
(select distinct OrderuserID from orders)
You can also do it with a left join:
select loginFirstName, loginLastName, loginEmailAddress
from login left join orders on loginID = OrderuserID
where OrderuserID is null
Not sure which will execute faster; give it a try. The first is easier to understand, IMHO.
EDIT: "select distinct" means "return me the set of unique values of the field". So, the subquery in the first SQL returns the set of users (their IDs) who do have orders. If a user has multiple orders, DISTINCT makes sure her ID is returned only once.
This should do it:
select *
from login l
left join orders o on l.loginId = o.OrderuserID
where o.OrderuserID is null
Try:
select login.loginFirstName, login.loginLastName, login.loginEmailAddress
FROM login
LEFT OUTER JOIN orders ON login.loginID = orders.OrderuserID
WHERE orders.OrderuserID IS NULL;
or something like that. I suspect the trick for a newer SQL user is the LEFT OUTER join. Without that specifier, a join will only return rows from the first table IF there are matches in the second. This way you get them all (and then filter out matches with the IS NULL phrase).
Though you should try first yourself and you could search on google first :):) .
Anyways you can use it in this way,
SELECT l.loginFirstName,l.loginLastName,
l.loginEmailAddress FROM login AS l LEFT JOIN orders as o
ON l.loginID = o.OrderuserID where OrderuserID is NULL