mysql select data from 3 tables using user input - mysql

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.

Related

Reuse MySQL subquery in various WHERE clause without subquery duplication

Extracting a set record ids from a translations table using a subquery.
Then need to feed this set of ids to several WHERE clauses of another query in order to extract the record from a specific table (product_listings) via a series of joins.
Table join structure
product_brands(1) <-> (n)products(1) <-> (n)product_categories(1) <-> (n)product_listings
The set of ids returned by the subquery can be for any of the 4 tables above.
Subquery returning the sets of ids
select
record_id
from
translations
where
translations.locale = 'en_CA'
and (
translations.table = 'product_listings'
or translations.table = 'product_categories'
or translations.table = 'products'
or translations.table = 'product_brands'
)
and MATCH (translations.translation) AGAINST ('+jack*' IN BOOLEAN MODE);
Main query here using the ids in WHERE clauses
select
product_listings.*
from
product_listings
left join product_categories on product_categories.ch_id = product_listings.ch_vintage_id
left join products on products.ch_id = product_categories.ch_product_id
left join product_brands on product_brands.ch_id = products.ch_brand_id
where
product_listings.ch_id in (5951765, 252242) <---| Replace these fixed ids
or product_categories.ch_id in (5951765, 252242) <---| with the "record_id" set
or products.ch_id in (5951765, 252242) <---| returned by the subquery
or product_brands.ch_id in (5951765, 252242); <---|
Both queries works perfectly independently. But cannot succesfully merge them into one.
Only dirty way I found is to repeat the subquery at each WHERE clause. Tried it and it works, but doubtfully the most effective and optimized way to do it.
Tried using variable, but only one value can be stored - unfortunately not a viable option.
Spent countless hours ressearching on how to avoid repeating a subquery and been rewriting those in many ways, but still can't get it to work.
Any suggestion on how to integrate the subquery elegantly and efficiently?
Currently working with Mysql Ver 14.14 Distrib 5.7.37, for Linux (x86_64)
UPDATE 2022/04/16: Adding sample data of translations table and expected results of both queries
Sample of the translations table with those 2 ids
+-----------+----------------+--------+-------------------------------+
| record_id | table | locale | translation |
+-----------+----------------+--------+-------------------------------+
| 5951765 | products | en_CA | Jack Daniel's |
| 252242 | product_brands | en_CA | Dixon's & Jack Daniel's |
+-----------+----------------+--------+-------------------------------+
Here is the subquery response
+-----------+
| record_id |
+-----------+
| 5951765 |
| 252242 |
+-----------+
And a the main query response (final expected results) using the set of hardcoded ids. I modified the select clause to return specific columns to make the table readable instead of the '*'.
First 2 columns are the located set of ids in the products and product_brands table and 2 other one are from the corresponding product_listings record extracted via the joins.
+------------+----------+--------------+-----------------+
| product_id | brand_id | listing_cspc | listing_format |
+------------+----------+--------------+-----------------+
| 5951765 | 5936861 | 798248 | 6x750 |
| 5951765 | 5936861 | 545186 | 6x750 |
| 5951956 | 252242 | 400669 | 12x750 |
| 5951955 | 252242 | 400666 | 12x750 |
| 5951701 | 252242 | 437924 | 12x750 |
| 5951337 | 252242 | 20244 | 6x750 |
| 5950782 | 252242 | 65166 | 12x750 |
| 5950528 | 252242 | 104941 | 12x750 |
| 5949763 | 252242 | 13990091 | 12x750 |
| 5949750 | 252242 | 614064 | 12x750 |
...
| 1729121 | 252242 | 280248 | 12x750 |
| 1729121 | 252242 | 36414 | 12x750 |
+------------+----------+--------------+-----------------+
As you can see, the ids from the subquery are matching different column. In this case 5951765 is the products.ch_id and the 252242 is the product_brands.ch_id.
Below is a visual representation of what I'm trying to achieve considering the current (1):(n) relations of the tables
Translations seems to be the driver for this so I would consider a view and drive from the view
Create view yoursubquery as vids;
select
product_listings.*
from vids
left join product_listings pn product_listings.id = vids.record_id
left join product_categories on product_categories.ch_id = product_listings.ch_vintage_id
left join products on products.ch_id = product_categories.ch_product_id
left join product_brands on product_brands.ch_id = products.ch_brand_id
Sample data would be good..
FINALLY! Got it to work.
With #P.Salmon suggestion to store the subquery result in a view, I then did a cross join on that view and use the results in the WHERE clause of the main query.
But that led me to now simply skip the view and the true final solution is to put the subquery in the cross join thus skipping the view.
Sleek and VERY performant.
Final query with subquery in the croos join
select
product_listings.*
from
product_listings
cross join (
select
record_id
from
translations
where
translations.locale = 'en_CA'
and (
translations.table = 'product_listings'
or translations.table = 'product_categories'
or translations.table = 'products'
or translations.table = 'product_brands'
)
and MATCH (translations.translation) AGAINST ('+jack*' IN BOOLEAN MODE)
) as vids
left join product_categories on product_categories.ch_id = product_listings.ch_vintage_id
left join products on products.ch_id = product_categories.ch_product_id
left join product_brands on product_brands.ch_id = products.ch_brand_id
where
product_listings.ch_id = vids.record_id
or product_categories.ch_id = vids.record_id
or products.ch_id = vids.record_id
or product_brands.ch_id = vids.record_id
order by
product_brands.ch_id desc,
products.ch_id desc;

Retrieve data from tables via JOINS

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
;

Generate table with columns dynamically

Say we have a table named questions with the following schema and content
id | name | text
1 | Q1 | Java: some text
2 | Q2 | Python: other text
3 | Q3 | C#: something else
then a table called answers with something like this
id | uid | qid | value
1 | 100 | 1 | true
2 | 100 | 3 | false
3 | 101 | 2 | false
4 | 101 | 3 | true
where qif is a FK to the question table, and uid a key to some other user table (not important here)
Now I'd like to generate the following table:
uid | Java | Python | C#
100 | true | null | false
101 | null | false | true
that is, a table that for each user contains all different questions taken from its table, with its column name generated dynamically based on its text (ie. using substring for instance)
My initial approach is the naive one, using something like this:
SELECT uid, java
FROM answer
LEFT JOIN
(SELECT a.uid, q.value AS "java"
FROM answer a
INNER JOIN question q ON q.id = a.qid
WHERE q.id=1) java
ON java.uid = u.id
LEFT JOIN
// same for "python", "c#"...
...
that is, generating each column by appending to it through a join. This of course works fine when having few items, but I was wondering if there is any alternative approach to do it more generically, without having to do these repetitive LEFT JOINS
Any help is greatly appreciated!

Mysql: how to return value or empty string from column ids in table (multi table)

I'M trying to extract all information into my table, but I need to change id, when available, to the name into another table.
I have 1 table like that:
|------------------------------|
|-id-|-systems-|-remote-|-deco-|
| 1 | NULL | 3 | |
| 2 | 21 | NULL | 2 |
|-------------------------------
each column like "systems" / "remote" / "deco" refer to an id into another table
I know how to use INNER JOIN. But if I use that, I got an empty result because the value need to be appears into the others tables.
ex.:
SELECT qd.id,s.name as systems,r.name as remote, d.name as deco
FROM `quote_data` qd
INNER JOIN systems s ON qd.systems=s.id
INNER JOIN remote r ON qd.remote=r.id
INNER JOIN deco d ON qd.deco=d.id
I got empty result.
In the best words, I need to do something like:
|------------------------------|
|-id-|-systems-|-remote-|-deco-|
| 1 | | R42 | |
| 2 | GTV | | B21 |
|-------------------------------
Also, I use innoDB table
Any Idea how to fix that?

How to write a proper If...Else Statement with JOIN in MySQL?

I'm quite a beginner in MySQL I just know the totally basic statements, however now I'ts time for me to get into some more difficult, but worth stuff.
I actually have 3 tables in MySQL, here is the representation:
users:
user_id | name | country
---------------------------
1 | Joseph | US
2 | Kennedy | US
3 | Dale | UK
admins:
admin_id | name | country
----------------------------
1 | David | UK
2 | Ryan | US
3 | Paul | UK
notes:
id | n_id | note | comment | country | type | manager
----------------------------------------------------------------
1 | 3 | This is the 1st note | First | US | admin | 2
2 | 2 | This is the 2nd note | Second | US | user | 1
3 | 2 | This is the 3rd note | Third | UK | user | 2
Now I would like to execute something like this SQL (I'm going to type not real commands here, because I'm not really familiar with all of the SQL expressions):
IF notes.type = admin
THEN
SELECT
notes.note,
notes.comment,
notes.country,
admins.name,
admins.country
FROM notes, admins
WHERE notes.n_id = admin.admin_id
ELSEIF notes.type = 'user'
SELECT
notes.note,
notes.comment,
notes.country,
users.name,
users.country
FROM notes, users
WHERE notes.n_id = users.user_id
I hope you understand what would I like to achieve here. I could do this easily with more SQL statements, but I would like to try some query which doesn't use that much resources.
Edit 1:
I would like to Get all of the Notes and get which usergroup has submitted it than apply the user's name to it. I mean, if the admin submitted the note, than SQL should choose the ID from the Admin table (as per the type value) but if a User submitted the note, it should get the name from the Users table.
The result should look something similar to this:
result:
------
id | note | comment | country | name
--------------------------------------------------------
1 | This is the 1st note | First | US | Paul
2 | This is the 2nd note | Second | US | Kennedy
3 | This is the 3rd note | Third | UK | Kennedy
Edit 2:
I have actually forgot to mention, that all of these should be listed to a manager. So a 'manager ID' should be added to the Notes and list all of the notes where the manager is for example: 2.
Here is a method that you can do in one query:
SELECT n.note, n.comment, n.country,
coalesce(a.name, u.name) as name, coalesce(a.country, u.country) as country
FROM notes n left join
admins a
on n.n_id = a.admin_id and n.type = 'admin' left join
users u
on n.n_id = u.user_id and n.type = 'user';
This uses left join to bring the records together from both tables. It then chooses the matching record for the select.
To select a particular manager, remove the semicolon and add:
where n.manager = 2;
If you expect admins and users in one result you have got several options. The simplest way is to make a union select like this:
SELECT
notes.note,
notes.comment,
notes.country,
admins.name,
admins.country
FROM
notes join admins on notes.n_id = admin.admin_id
WHERE
notes.manager = 2
UNION ALL
SELECT
notes.note,
notes.comment,
notes.country,
users.name,
users.country
FROM
notes join users on notes.n_id = users.user_id
WHERE
notes.manager = 2