I have two simple tables:
user
+-------------------------------------------------+
| uid | firstname | lastname |
|-------------------------------------------------|
| 4000 | Zak | Me |
+-------------------------------------------------+
user_role
+----------------------------------------------+
| rid | uid | oid |
|----------------------------------------------|
| 5 | 4000 | 7000 |
+----------------------------------------------+
I am using this query
SELECT us.firstname, us.lastname, ur.oid
FROM user us
LEFT JOIN user_role ur
ON us.uid = ur.uid
WHERE us.firstname = 'Zak';
My result is
+-------------------------------------------------+
| firstname | lastname | oid |
|-------------------------------------------------|
| Zak | Me | (null) |
+-------------------------------------------------+
What am I missing? It has to be something simple!
UPDATE
Has to do something with the WHERE clause .. Because if left out, it returns all rows with oid included
Follow a process like this:
run this query
SELECT *
FROM user us
WHERE us.firstname = 'Zak';
If you get a result the Where clause is fine. So now you run:
SELECT *
FROM user us
LEFT JOIN user_role ur
ON us.uid = ur.uid
WHERE us.firstname = 'Zak';
If yuo get no records then there is something wrong with the join. Could be that they have some unprintable characters and 4000 <>4000 as a result. So let's check that.
select * FROM user us where us.uid = 4000
select * FROM user_role us where us.uid = 4000
If one of then does not return a result then there is a data problem where one of the fields contains unprintable characters.
If the select * works, then try the original query again only add a few other fields from the user_role table such as the uid. Then you can see if the join is working but the field is empty or if the join is wrong or possibly you are looking at the wrong field.
SELECT us.firstname, us.lastname, ur.oid, ur.uid
FROM user us
LEFT JOIN user_role ur
ON us.uid = ur.uid
WHERE us.firstname = 'Zak';
It is also possible the join fields are different datatypes and some type of implicit conversion is messing them up. In that case you probably want to explicitly convert or preferably design your table so that they both use the same data type as they appear to be in a PK/FK relationship.
Related
Let's say I have a user and preference table, as well as a bridge table user_preference between the two:
/* user table: */
+----------+--------------+
| Field | Type |
+----------+--------------+
| id | int |
| username | varchar(255) |
+----------+--------------+
/* preference table: */
+------------+--------------+
| Field | Type |
+------------+--------------+
| preference | varchar(255) |
+------------+--------------+
/* user_preference table: */
+-----------------+--------------+
| Field | Type |
+-----------------+--------------+
| user_id | int |
| preference_name | varchar(255) |
+-----------------+--------------+
For instance there are 3 preferences to choose from: "swimming", "watching TV", "cycling". And one user can have zero or all 3 of the preferences, which is reflected on the user_preference table.
Now I want to query 10 different users, and with all of them each of their own preferences included, either null or mutiple preferences, how to construct a select statement for that?
So far I have tried something like this:
SELECT u.*, p.preference_name
FROM user u
LEFT JOIN user_preference p ON p.user_id = u.id
LIMIT 10;
/* Result: */
id | username | preference_name
1 | user1 | swimming
1 | user1 | cycling
2 | user2 | null
3 | user3 | watching TV
... /* rest of the result */
As you can see the result will return a duplicate user1, and it won't be 10 distinct users. I'm aware of the distinct and group by keywords, it doesn't solve the problem, as it will only return a single preference for a user, while the user can have multiple preferences.
How to do that with one single select statement?
Try this.
SELECT u.*,
GROUP_CONCAT(DISTINCT p.preference_name) AS prefs
FROM user u
LEFT JOIN user_preference p ON p.user_id = u.id
GROUP BY u.id
LIMIT 10;
The GROUP_CONCAT() will make a comma-separated list of preferences for each user.
Pro tip. When tables get very large, altering ENUMs to add more values gets very time-consuming. Plus, it's usually unwise to design a database so it needs lots of ALTER TABLE statements as it grows. So, the approach you have outlined is the right way to go if you want your possible preferences to be open-ended.
I have three tables: sessions, urls and visitors.
I need to join these three tables in such a way that I should be able to get data from each table and the maximum number of rows returned should be equal to sessions.
Following is the basic schema of my tables.
Table sessions
session_id | url_id | referrer_id | country
-------------------------------------------
1234 | a1b1 | bb11 | US
4567 | x1y1 | ll33 | IN
6789 | a1b1 | ff99 | UK
Table urls
id | url |
-----------------------------------------
a1b1 | https://url-1.com |
x1y1 | https://url-2.com |
bb11 | https://referrer-url-1.com |
ll33 | https://referrer-url-2.com |
ff99 | https://referrer-url-3.com |
Table visitors
id | session_id | visiting_time |
-----------------------------------------
1 | 1234 | 447383930 |
2 | 4567 | 547383930 |
3 | 6789 | 647383930 |
What I want as the final output should look like:
session_id | visiting_time | url | referrer_url | country
------------------------------------------------------------------------------------------
1234 | 447383930 | https://url-1.com | https://referrer-url-1.com | US |
4567 | 547383930 | https://url-2.com | https://referrer-url-2.com | IN |
6789 | 647383930 | https://url-1.com | https://referrer-url-3.com | UK |
I want to map url_id in sessions table with id in urls table and get the corresponding url from urls table and have the value in the new column named url. Similarly, map referrer_id in sessions table with id in urls table and get the corresponding url from urls table and have the value in the new column named referring_url.
As you can see: JOINS with sessions and visitors is simple and can be simply done via:
select session_id, visiting_time, country
from sessions,
visitors
where sessions.session_id = visitors.session_id;
But joining with urls table and getting the url and referring_url is somewhat tricky. I have tried LEFT JOIN and INNER JOIN but couldn't make it work.
Any help with query or references would be helpful.
Thanks!
You should avoid using comma based Implicit joins and use Explicit Join based syntax
You will need two joins with urls table; one to fetch the url and another for referrer_url.
Try the following:
SELECT s.session_id,
v.visiting_time,
u1.url,
u2.url AS referrer_url,
s.country
FROM sessions AS s
JOIN visitors AS v ON v.session_id = s.session_id
JOIN urls AS u1 ON u1.id = s.url_id
JOIN urls AS u2 ON u2.id = s.referrer_id
select sessions.session_id, visitors.visiting_time, urls.url, urlsReferrer.url referrer_url, sessions.country
from sessions
inner join visitors on sessions.session_id = visitors.session_id
inner join urls on sessions.url_id = url.id
left join urls urlsReferrer on sessions.referrer_id = urlsReferrer.id
You should use a join on urls twice one of url_id and one for referrer_id
select session_id
, visiting_time
, u1.url
, u2.url
, country
from sessions
INNER JOIN visitors ON sessions.session_id = visitors.session_id
INNER JOIN urls u1 on u1.id= sessions.url_id
INNER JOIN urls u2 on u2.id= sessions.referrer_id
In this way you can join the sessions for retrive both then values you need
Joins are defined in the from statement - please read up on https://www.w3schools.com/sql/default.asp to better get a sense of join usage.
Modify the query as needed based on which table "referrer_url" actually comes from
Warning: You must include a where statement which limits your result. I strongly suggest defining a date field and range to prevent you from initiating a long running query and affecting database performance.
see below for query
select
s.session_id,
v.visiting_time,
s.country,
u.url,
u.referrer_url
from
sessions s
join visitors v on session_id
join urls on u.id=s.url_id
;
I want to count all entries in one table grouped by the user id.
This is the query I used which works fine.
select uuid_mapping_id, count(*) from t_message group by uuid_mapping_id;
and these are the results:
+-----------------+----------+
| uuid_mapping_id | count(*) |
+-----------------+----------+
| 1 | 65 |
| 4 | 277 |
Now I would like to display the actual user name, instead of the ID.
To achieve this I would need the help of two different tables.
The table t_uuid_mapping which has two columns:
uid_mapping_id, which equals uuid_mapping_id in the other table.
And f_uuid which is also unique but completely different.
f_uuid can also be found in another table t_abook which also contains the names in the column f_name.
The result I am looking for should be:
+-----------------+----------+
| f_name | count(*) |
+-----------------+----------+
| admin | 65 |
| user1 | 277 |
I am new to the database topic and understand that this could be achieved by using JOIN in the query, but to be honest I did not completely understand this yet.
if I understand you correctly:
SELECT tm.f_name, COUNT(*) as count
FROM t_message tm
LEFT JOIN t_abook ta ON (tm.uuid_mapping_id = ta.uid_mapping_id)
GROUP BY tm.f_name
I appreciate there are many tutorials and examples out there explaining JOIN concepts- however I am struggling to apply the examples to my specific scenario. I would appreciate some help, and if it isn't too much to ask- a break down of what is going on in the solution to achieve the desired results.
3 Tables: users, assessments, assessment_log.
users
+--------+------+------+-----------+----------+-----------+-------+--------+-----------+---------------+------+----------+
| UserID | User | Pass | FirstName | LastName | LastLogin | Email | Mobile | Kenitalla | AccountStatus | Role | Operator |
+--------+------+------+-----------+----------+-----------+-------+--------+-----------+---------------+------+----------+
assessments
+--------------+------+-----------+-----------+-----------+-----------+-----------+--------------+----------+---------+----------+
| AssessmentID | Name | Criteria1 | Criteria2 | Criteria3 | Criteria4 | Criteria5 | RoleRequired | Required | Renewal | Operator |
+--------------+------+-----------+-----------+-----------+-----------+-----------+--------------+----------+---------+----------+
assessment_logs
+-----------------+------+----------------+------------+--------+-----------+----------+----------+----------+----------+----------+----------+---------+---------------+---------+
| AssessmentLogID | Date | AssessmentName | AssessedBy | UserID | StaffName | Comments | Verdict1 | Verdict2 | Verdict3 | Verdict4 | Verdict5 | Verdict | RenewalPeriod | NextDue |
+-----------------+------+----------------+------------+--------+-----------+----------+----------+----------+----------+----------+----------+---------+---------------+---------+
When a user takes an assessment, an entry goes in to the assessment_log. Some assessments are required (true or false in Required column) where as some are optional. The RoleRequired column stipulates which user Role the assessment is required for.
I would like to generate a list of users and assessments they are yet to pass. The assessments must be required, and the required role must match the users role. An absense of an entry into the assessment_log with a "Pass" in the Verdict column indicates the assessment has not yet been passed.
In plain speak, I am looking for a query that will achieve the following result:
+------------------+-----------------+----------------+
| assessments.Name | users.FirstName | users.LastName |
+------------------+-----------------+----------------+
Where the assessment is required (Required equals true), the RequiredRole matches the users.Role column, and there is no entry in the assessments_log for the assessment where the Verdict column holds a "Pass" value.
Please let me know if further clarification is required.
Thanks in advance!
I'd suggest using a LEFT JOIN.
First, we generate a cross product of assessments and users, which contains for each user, all assessments that are required for their role.
Then, a LEFT JOIN checks if these assessments were passed.
SELECT ...
FROM
users u
JOIN assessments a ON (a.RoleRequired = u.Role)
LEFT JOIN assessment_logs al ON (
al.AssessmentID = a.AssessmentID
AND al.UserID = u.UserID
AND al.Verdict='Pass'
)
WHERE
a.required
AND al.UserID IS NULL
Your attribute names are inconsistent between tables and, although I don't see any duplicate names, it's probably only a matter of time before you do, so I suggest you fix this.
In the meantime, here's how to rename the columns on the fly in order to simplify the join syntax:
SELECT AssessmentName, UserLastName, UserFirstName
FROM ( SELECT FirstName AS UserFirstName, Lastname AS UserLastName, Role
FROM users ) u
NATURAL JOIN
{ SELECT Name AS AssessmentName, RoleRequired AS Role
FROM assessments
WHERE Required ) a
NATURAL JOIN
( SELECT AssessmentName
FROM assessment_logs
WHERE Verdict = 'Pass' ) l;
I have 3 tables with the following columns and values (they have multiple entries but I'm showing you one):
protein
+--------+------+-------------+
| pdb_id | name | description |
+--------+------+-------------+
| 1AF6 | porin| maltoporin |
+--------+------+-------------+
organism:
+--------+-----------------------+
| org_id | organismName |
+--------+-----------------------+
| 4 | Comamonas acidovorans |
+--------+-----------------------+
protein_organism:
+--------+--------+
| pdb_id | org_id |
+--------+--------+
| 1AF6 | 4 |
+--------+--------+
I'm making a website where someone can see all the proteins from a specific organism that can be selected from a drop down menu.
However when I try to fetch the data the browser goes to the correct url: http://localhost:8084/response_organism?organismName=Comamonas+acidovorans
but shows nothing.
This is my sql command:
query = "SELECT * FROM protein JOIN protein_organism ON protein_organism.pdb_id = protein.pdb_id JOIN organism ON organism.org_id = protein_organism.org_id WHERE organism.organismName="+po;
po (string) is the user input fetched from my index.jsp form
What is wrong with my sql command?
You are querying the wrong way. This is the correct way of query with JOIN.
"SELECT *
FROM protein P
INNER JOIN protein_organism PO
ON PO.pdb_id = P.pdb_id
LEFT JOIN organism O
ON O.org_id = PO.org_id
WHERE O.organismName="+po;
Make appropriate changes to this query if you have some error or different result when run it.