MySQL substitution - mysql

+-----------------------------+
| tables |
+-----------------------------+
| Date |
| IP |
| Location |
| UserAgent |
+-----------------------------+
For simplicty assume that these four tables have only two columns: ID(int), name(VARCHAR)
Then I have this table called access_log, where I store only IDs of items i have in other four tables
+---------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+---------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| ip_id | int(11) | NO | | NULL | |
| ua_id | int(11) | NO | | NULL | |
| path_id | int(11) | NO | | NULL | |
| date_id | int(11) | NO | | NULL | |
+---------+---------+------+-----+---------+----------------+
Let's say I want to select everything from this access_log table and replace ids with column NAME I have in four other tables.
How can I achive that?

You need to join on all four tables:
SELECT access_log.id,
`date`.name AS date_name,
ip.name AS ip_name,
location.name AS location_name,
useragent.name AS useragent_name
FROM access_log
OUTER JOIN `date` ON `date`.id = access_log.date_id
OUTER JOIN ip ON ip.id = access_log.ip_id
OUTER JOIN location ON location.id = access_log.path_id
OUTER JOIN useragent ON useragent.id = access_log.ua_id

Related

Debugging a rather difficult/complex MySQL query

I'm having troubles in making a rather difficult MySQL query work. I've been trying, but creating complex queries has never been my strong side.
This query includes 4 tables, which I'll describe of course.
First, we have song table, which I need to select the needed info from.
+--------------+-----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-----------+------+-----+---------+----------------+
| ID | int(6) | NO | PRI | - | auto_increment |
| Anime | char(100) | NO | | - | |
| Title | char(100) | NO | | - | |
| Type | char(20) | NO | | - | |
| Singer | char(50) | NO | | - | |
| Youtube | char(30) | NO | | - | |
| Score | double | NO | | 0 | |
| Ratings | int(8) | NO | | 0 | |
| Favourites | int(7) | NO | | 0 | |
| comments | int(11) | NO | | 0 | |
| release_year | int(4) | NO | | 2019 | |
| season | char(10) | NO | | Spring | |
+--------------+-----------+------+-----+---------+----------------+
Then we have song_ratings, which basically represents the lists of each user, since once you rate a song, it appears on your list.
+------------+----------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------+------+-----+-------------------+----------------+
| ID | int(11) | NO | PRI | 0 | auto_increment |
| UserID | int(11) | NO | MUL | 0 | |
| SongID | int(11) | NO | MUL | 0 | |
| Rating | double | NO | | 0 | |
| RatedAt | datetime | NO | | CURRENT_TIMESTAMP | |
| Favourited | int(1) | NO | | 0 | |
+------------+----------+------+-----+-------------------+----------------+
Users have the option to create custom lists(playlists), and this is the table which they are stored in. This is table lists.
+------------+-----------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-----------+------+-----+-------------------+----------------+
| ID | int(11) | NO | PRI | 0 | auto_increment |
| userID | int(11) | NO | MUL | 0 | |
| name | char(50) | NO | | - | |
| likes | int(11) | NO | | 0 | |
| favourites | int(11) | NO | | 0 | |
| created_at | datetime | NO | | CURRENT_TIMESTAMP | |
| cover | char(100) | NO | | - | |
| locked | int(1) | NO | | 0 | |
| private | int(1) | NO | | 0 | |
+------------+-----------+------+-----+-------------------+----------------+
And finally, the table which contains all the songs that have been added to any playlists, called list_elements.
+--------+---------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+----------------+
| ID | int(11) | NO | PRI | 0 | auto_increment |
| listID | int(11) | NO | MUL | 0 | |
| songID | int(11) | NO | MUL | 0 | |
+--------+---------+------+-----+---------+----------------+
What my query needs to do is list all the songs that are on the list of a user, basically these are the record in song_ratings where the userID = ?(obviously the ID of the user), but are not on a specific playlist(has no record in list_elements) where the ID/listID = ?(the ID of that playlist).
This is the query I've been using so far, but after a while I had realized this doesn't actually work the way I wanted to.
SELECT DISTINCT
COUNT(*)
FROM
song
INNER JOIN song_ratings ON song_ratings.songID = song.ID
LEFT JOIN list_elements ON song_ratings.songID = list_elements.songID
WHERE
song_ratings.userID = 34 AND list_elements.songID IS NULL
I have also tried something like this, and several variants of it
SELECT DISTINCT
COUNT(*)
FROM
song
INNER JOIN song_ratings ON song_ratings.songID = song.ID
INNER JOIN lists ON lists.userID = song_ratings.userID
LEFT JOIN list_elements ON song_ratings.songID = list_elements.songID
WHERE
song_ratings.userID = 34 AND lists.ID = 1
To make it easier, here's a SQL Fiddle, with all the necessary tables and records in them.
What you need to know. When you check for the playlist with the ID of 1, the query needs to return 23(basically all matches).
When you do the same with the ID 4, it need to return 21, if the query works correctly, because the playlist 1 is empty, thus all of the songs in the table song_ratings can be added to it(at least the ones that exist in song table, which is only half of the overall records now).
But playlist 4 already has 2 songs added to it, so only 21 are left available for adding.
Or in case the number are wrong, playlist 1 needs to return all matches. playlist 4 need to return all matches-2(because 2 songs are already added).
The userID needs to remain the same(34), and there are no records with different ID, so don't change it.
You could try subquery with NOT IN clause
SELECT DISTINCT
COUNT(*)
FROM
song
INNER JOIN song_ratings ON song_ratings.songID = song.ID
WHERE
song_ratings.userID = 34 AND song.ID not in (select songID from list_elements group by songID)
Your original query was almost correct. When you use a column from a joined table with a LEFT JOIN in the WHERE-clause, it causes the LEFT JOIN to turn into an INNER JOIN.
You can put the condition into the ON-clause:
SELECT COUNT(*)
FROM song
INNER JOIN song_ratings ON song_ratings.songID = song.ID
LEFT JOIN list_elements ON song_ratings.songID = list_elements.songID
AND list_elements.songID IS NULL
WHERE song_ratings.userID = 34
Using JOINs in MySQL is faster than using subqueries, this would probably be faster as well.
Btw, you do not need DISTINCT when you only have COUNT(*). The COUNT(*) returns only one row so there is no need to take distinct values from one value.

SQL, Select multiple values based on foreign key, return the values

How to select each team member name in a query, pilot and copilot based on the two foreign keys in team table
Table: player
+----------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+------------------------+-----+---------+----------------+
| id | int(10) unsigned| NO | PRI | NULL | auto_increment |
| name | varchar(60) | NO | | NULL | |
| isPilot | TINYINT(1) | NO | | NULL | |
| age | int(4) | NO | | NULL | |
+----------------+-----------------+------+-----+---------+----------------+
table: team
+----------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+-----------------+------+-----+---------+----------------+
| id | int(10) unsigned| NO | PRI | NULL | auto_increment |
| pilot_id | int(10) unsigned| NO | FK | NULL | |
| copilot_id | int(10) unsigned| NO | FK | NULL | |
+----------------+-----------------+------+-----+---------+----------------+
Is this a good database setup?
Might be a repost of SQL: Foreign Key With multiple child values , but can't apply it to mine
MCVE: https://www.db-fiddle.com/f/a2fCdy6RFqgReuL8FThhDP/2
Result should be something like
+----------------+-----------------+
| Pilot | Co-Pilot |
+----------------+-----------------+
| player4Name | player3Name |
| player2Name | player1Name |
+----------------+-----------------+
Try this :
select p1.name, p2.name from player p1
join team t1 on p1.id = t1.pilot_id
join player p2 on p2.id = t1.copilot_id;
It seems like you need two simple JOINs :
SELECT
p.name AS pilot,
cp.name AS copilot
FROM
team AS t
INNER JOIN player AS p on p.id = t.pilot_id
INNER JOIN player AS cp on cp.id = t.copilot_id

MYSQL Joins where conditions may be null

I'm having an issue with a query using INNER JOIN.
I have two tables. I need the department name and all three approvers. If any of the approvers are NULL, I need that displayed also.
mysql> desc department;
+-------------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+----------+------+-----+---------+----------------+
| id | int(8) | NO | PRI | NULL | auto_increment |
| departmentName | tinytext | YES | | NULL | |
| primaryApprover | int(8) | YES | | NULL | |
| secondaryApprover | int(8) | YES | | NULL | |
| tertiaryApprover | int(8) | YES | | NULL | |
+-------------------+----------+------+-----+---------+----------------+
mysql> desc approver;
+------------------+------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+------------+------+-----+---------+----------------+
| id | int(8) | NO | PRI | NULL | auto_increment |
| approverName | tinytext | YES | | NULL | |
| approverPosition | tinytext | YES | | NULL | |
| approverLogonId | tinytext | YES | | NULL | |
| approverEmail | tinytext | YES | | NULL | |
| isActive | tinyint(1) | YES | | NULL | |
+------------------+------------+------+-----+---------+----------------+
The following query works, but it does not give me data where the primary or secondary approver are NULL:
SELECT
a.departmentName as DEPARTMENT,
pa.approvername as PRIMARY,
sa.approvername as SECONDARY,
ta.approvername as TERTIARY
FROM
department as a
INNER JOIN
approver pa on a.primaryapprover=pa.id
INNER JOIN
approver sa on a.secondaryapprover = sa.id
INNER JOIN
approver ta on a.tertiaryapprover = ta.id
ORDER BY
a.departmentname;
Using this query, I get this result:
+--------------------------------+---------------------------+---------------------------+------------------------+
| DEPARTMENT | PRIMARY_APPROVER | SECONDARY_APPROVER | TERTIARY_APPROVER |
+--------------------------------+---------------------------+---------------------------+------------------------+
| Facilities | Washburn, Hoban | Cobb, Jayne | Reynolds, Malcomn |
| Personnel / HR | Frye, Kaylee | Serra, Inara | Book, Dariel |
+--------------------------------+---------------------------+---------------------------+------------------------+
2 rows in set (0.00 sec)
but should get this result:
+--------------------------------+---------------------------+---------------------------+------------------------+
| DEPARTMENT | PRIMARY_APPROVER | SECONDARY_APPROVER | TERTIARY_APPROVER |
+--------------------------------+---------------------------+---------------------------+------------------------+
| Business Office | NULL | Rample, Fanty | Niska, Adelei |
| Facilities | Washburn, Hoban | Cobb, Jayne | Reynolds, Malcomn |
| Personnel / HR | Frye, Kaylee | Serra, Inara | Book, Dariel |
| Technical Services | Tam, River | NULL | Tam, Simon |
+--------------------------------+---------------------------+---------------------------+------------------------+
4 rows in set (0.00 sec)
I'm not good at joins to begin with....what am I missing here?
Just use LEFT JOINS
SELECT
a.departmentName as DEPARTMENT,
pa.approvername as PRIMARY,
sa.approvername as SECONDARY,
ta.approvername as TERTIARY
FROM
department as a
LEFT JOIN
approver pa on a.primaryapprover=pa.id
LEFT JOIN
approver sa on a.secondaryapprover = sa.id
LEFT JOIN
approver ta on a.tertiaryapprover = ta.id
ORDER BY
a.departmentname;
INNER JOIN - keeps only records that match from both sides .
LEFT JOIN - keeps all the records from the left table, and only the record matching from the right table.
You can also use COALESCE to replace null values with a default value like '-1' or something.

Getting a SQL query to print 0 for null count results across 3 tables

I'm trying to get a SQL query to give me the results of a count but I need the result to include rows where the count is 0. What I found for solutions to this was to use IFNULL(COUNT(*), 0) in place of COUNT(*) however that had no effect on the result. I also tried using a LEFT JOIN but SQL gave me a syntax error if I tried to put in those. Here's my table setup
User
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| UserID | mediumint(9) | NO | PRI | NULL | auto_increment |
| firstName | varchar(15) | NO | | NULL | |
| lastName | varchar(15) | NO | | NULL | |
| Protocol | varchar(10) | NO | | NULL | |
| Endpoint | varchar(50) | NO | | NULL | |
| UsergroupID | mediumint(9) | NO | MUL | NULL | |
+-------------+--------------+------+-----+---------+----------------+
Subscription
+----------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+----------------+
| SubscriptionID | mediumint(9) | NO | PRI | NULL | auto_increment |
| TopicID | mediumint(9) | NO | MUL | NULL | |
| UserID | mediumint(9) | NO | MUL | NULL | |
+----------------+--------------+------+-----+---------+----------------+
Topic
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| TopicID | mediumint(9) | NO | PRI | NULL | auto_increment |
| Name | varchar(50) | NO | | NULL | |
| FBName | varchar(30) | YES | | NULL | |
| FBToken | varchar(255) | YES | | NULL | |
| TWName | varchar(10) | YES | | NULL | |
| TWToken | varchar(50) | YES | | NULL | |
| TWSecret | varchar(50) | YES | | NULL | |
+----------+--------------+------+-----+---------+----------------+
My SQL query to try and get the COUNT is :
SELECT Topic.TopicID as ID, Topic.Name AS TopicName, COUNT(*) AS numSubscriptions
FROM User, Topic, Subscription
WHERE Subscription.UserID = User.UserID
AND Subscription.TopicID = Topic.TopicID
GROUP BY Topic.TopicID;
I've tried replacing COUNT(*) with IFNULL(COUNT(*), 0) and I've tried to replace User,Topic,Subscription with User JOIN Subscription JOIN Topic and I also tried User LEFT JOIN Subscription LEFT JOIN Topic but that got a SQL error.
The output I'm getting is:
+----+-----------+------------------+
| ID | TopicName | numSubscriptions |
+----+-----------+------------------+
| 2 | test | 2 |
| 3 | test2 | 1 |
+----+-----------+------------------+
I need to be getting
+----+-----------+------------------+
| ID | TopicName | numSubscriptions |
+----+-----------+------------------+
| 2 | test | 2 |
| 3 | test2 | 1 |
| 4 | test3 | 0 |
+----+-----------+------------------+
By default, outer joins are left to right. So, the trick is to start with Topic:
SELECT Topic.TopicID as ID, Topic.Name AS TopicName,
COUNT(User.UserID) AS numSubscriptions
FROM Topic
LEFT JOIN Subscription
ON Subscription.TopicID = Topic.TopicID
JOIN User
ON User.UserID = Subscription.UserID
GROUP BY Topic.TopicID
This allows for multiple subscriptions per user and requires that the user record exists to be considered in the count.
COUNT(NULL) evaluates to 0, so any topic records without a corresponding subscription and user record will show as 0.
If you're not concerned whether the user record exists, you could simplify it to the following:
SELECT Topic.TopicID as ID, Topic.Name AS TopicName,
COUNT(Subscription.TopicID) AS numSubscriptions
FROM Topic
LEFT JOIN Subscription
ON Subscription.TopicID = Topic.TopicID
GROUP BY Topic.TopicID
The example below should do what you're after. The column in the COUNT() can be any column of the subscription table, but using its ID is a good practice.
Using the left join ensures that all entries of the user table will show up in the results, even if there are no matching subscriptions.
SELECT User.firstName,
User.lastName,
Topic.Name AS TopicName,
COUNT(Subscription.SubscriptionId) AS numSubscriptions
FROM USER
LEFT OUTER JOIN Subscription ON Subscription.UserID=USER.UserID
LEFT OUTER JOIN Topic ON Subscription.TopicID=Topic.TopicID
GROUP BY User.firstName, User.lastName, Topic.Name;

How to link 4 tables using an mySQL query

Vehicle
+--------------------+--------------+------+-----+---------+
| Field | Type | Null | Key | Default |
+--------------------+--------------+------+-----+---------+
| id | int(11) | NO | Pk | NULL |
| model | varchar(35) | NO | | NULL |
+--------------------+--------------+------+-----+---------+
info
+--------------------+--------------+------+-----+---------+
| Field | Type | Null | Key | Default |
+--------------------+--------------+------+-----+---------+
| id | int(11) | NO | Pk | NULL |
| vehicle_id | varchar(35) | NO | FK | NULL |
| location | varchar(35) | NO | | NULL |
+--------------------+--------------+------+-----+---------+
Axle 1
+--------------------+--------------+------+-----+---------+
| Field | Type | Null | Key | Default |
+--------------------+--------------+------+-----+---------+
| id | int(11) | NO | Pk | NULL |
| vehicle_id | varchar(35) | NO | FK | NULL |
| weight | varchar(35) | NO | | NULL |
+--------------------+--------------+------+-----+---------+
Axle 2
+--------------------+--------------+------+-----+---------+
| Field | Type | Null | Key | Default |
+--------------------+--------------+------+-----+---------+
| id | int(11) | NO | Pk | NULL |
| vehicle_id | varchar(35) | NO | FK | NULL |
| weight | varchar(35) | NO | | NULL |
+--------------------+--------------+------+-----+---------+
I wish to create a query that will return all the fields from all tables with a common vehicle_id. The vehicle_id being a reference in each of the 3 tables (info, axle1, axle 2) to the primary key in the Vehicle table. Could someone please explain how I might go about doing so? I tried using multiple joins but it didnt work!Many thanks.
EDIT:
Query I tried was;
SELECT *
FROM Vehicle
JOIN info, axle1, axle 2
ON vehicle.id = axle1.vehicle_id
AND vehicle.id = axle2.vehicle_id AND vehicle.id = info.vehicle_id
Try this instead:
SELECT *
FROM Vehicle v
INNER JOIN Info i ON v.id = i.vehicle_id
INNER JOIN Axle1 a1 ON i.vehicle_id = a1.vehicle_id
INNER JOIN Axle2 a2 ON a1.vehicle_id = a2.vehicle_id
Check this link: mySQL get information from multiple tables in one query I think there's an example that may fit
You just need to specify an identifier for each table, so you can specify which table are you talking about when you refer to a field. Then, make sure you link all the id's as a condition so you leave out all the combinations where identifiers don't match. That would be:
SELECT * FROM Vehicle v, Info i, Axle1 a1, Axle2 a2 WHERE v.id == a1.vehicle_id AND v.id == a2.vehicle_id AND v.id == i.vehicle_id