Select rows where join table has same value - mysql

product_options
ID | Option
1 | One
2 | Two
3 | Three
product_to_options
ID | Product | Option
1 | 1 | 1
2 | 2 | 2
3 | 2 | 3
Essentially, I need to get all the rows from product_options, apart from the
ones that are supplied where the options are on the same products.
For example:
Passing the value of 1, would return nothing.
Passing the value of 2, would return the option with ID 3
Passing the value of 3, would return the option with ID 2.

I did my best with what I understand.
pto.Product = #option so you get all the rows match #option
pto.Option <> #option but filter those with the same ID
.
SELECT po.*
FROM product_options po
JOIN (
SELECT pto.Option
FROM product_to_options pto
WHERE pto.Product = #option
AND pto.Option <> #option
) as filter
ON PO.ID = filter.Option

You can join a table to itself, filter the left table on the value and return the value from the right table.
SELECT
pto2.Option
FROM
product_to_options pto1
INNER JOIN product_to_options pto2
ON pto1.Product = pto2.Product
AND pto1.ID <> pto2.ID
WHERE
pto1.option = #option;
You should note that you can get more than one value back depending on the data.
Here's a working sample with minor syntax changes for sql server (you'll need to check text-only-results or click through the grid)

Related

SELECT rows where column value is NEVER x

I have a table which is linked to another table.
One table holds carriers, and another holds routes for the carriers.
The routes table has a carrier_id column which ties one with another, and an status field which determines the route's activity status, 0 being the active status.
I am trying to list carriers which have no active routes, and this has put me in a precarious situation, where carriers without routes at all are selected easily, but the second party of the query causes trouble for me.
SELECT c.id
, c.title
FROM carriers c
LEFT
JOIN routes r
ON r.carrier_id = c.id
WHERE r.route_id IS NULL
OR (r.status > 0 AND r.carrier_id = c.id)
The problem with this is fairly obvious - the resulting table gets false positives - carriers who have archived AND unarchived routes. I'm pretty sure SQL employs some sort of construction I can use to specify something like this:
if any of carrier.routes.status == 0 exclude carrier from selection
And this is pretty much what the question boils down to.
Update: I've been asked for a dataset and an expected result for that data set, so I'm providing it below:
carriers:
--------------
| id | title |
--------------
| 1 | foo |
| 2 | bar |
| 3 | baz |
--------------
routes:
----------------------------
| id | carrier_id | status |
----------------------------
| 1 | 1 | 0 |
| 2 | 1 | 1 |
| 3 | 2 | 1 |
----------------------------
With the following data, carriers 2 and 3 should be returned, seeing as 3 has no active routes, and neither has 2. 1, however, has an active route, and thus is excluded from this selection.
Try like this
SELECT carrier.id, carrier.title
FROM carriers LEFT JOIN routes
ON routes.carrier_id = carrier.id and
(route.route_id IS NULL OR (route.status > 0)
Please note that Where (route.route_id IS NULL OR (route.status > 0) clause implicitly converts your left join to inner join
Better and cleaner solution
Select * from carriers
Where exists
(
Select 1 from routes where routes.carrier_id = carrier.id and status != 0
) or carriers.route_id is null.
OP's note: what I actually found working for me is based on the logic above, and goes like this:
Select * from carriers
Where exists
(
Select 1 from routes where routes.carrier_id = carrier.id and status != 0
) and not exists
(
Select 1 from routes where routes.carrier_id = carrier.id and status != 0
) or carriers.route_id is null.
You may need to try Left Outer Join which will give those records in left side table not present in the right side. So, all the Carriers with no records will be displayed. Now you just need to add one condition of routes.status > 0 for records with routes having active status. Like below:
SELECT carrier.id, carrier.title FROM carriers
LEFT JOIN routes ON routes.carrier_id = carrier.id
WHERE routes.status > 0

Issue with mysql query that calls column name from another table

I have two tables, one is an index (or map) which helps when other when pulling queries.
SELECT v.*
FROM smv_ v
WHERE (SELECT p.network
FROM providers p
WHERE p.provider_id = v.provider_id) = 'RUU='
AND (SELECT p.va
FROM providers p
WHERE p.provider_id = v.provider_id) = 'MjU='
LIMIT 1;
Because we do not know the name of the column that holds the main data, we need to look it up, using the provider_id which is in both tables, and then query.
I am not getting any errors, but also no data back. I have spent the past hour trying to put this on sqlfiddle, but it kept crashing, so I just wanted to check if my code is really wrong, hence the crashing?
In the above example, I am looking in the providers table for column network, where the provider_id matches, and then use that as the column on smv.
I am sure i have done this before just like this, but after the weekend trying I thought i would ask on here.
Thanks in Advance.
UPDATE
Here is an example of the data:
THis is the providers, this links so no matter what the name of the column on the smv table, we can link them.
+---+---+---------------+---------+-------+--------+-----+-------+--------+
| | A | B | C | D | E | F | G | H |
+---+---+---------------+---------+-------+--------+-----+-------+--------+
| 1 | 1 | Home | network | batch | bs | bp | va | bex |
| 2 | 2 | Recharge | code | id | serial | pin | value | expire |
+---+---+---------------+---------+-------+--------+-----+-------+--------+
In the example above, G will mean in the smv column for recharge we be value. So that is what we would look for in our WHERE clause.
Here is the smv table:
+---+---+-----------+-----------+---+----+---------------------+-----+--+
| | A | B | C | D | E | F | value | va |
+---+---+-----------+-----------+---+----+---------------------+-----+--+
| 1 | 1 | X2 | Home | 4 | 10 | 2016-09-26 15:20:58 | | 7 |
| 2 | 2 | X2 | Recharge | 4 | 11 | 2016-09-26 15:20:58 | 9 | |
+---+---+-----------+-----------+---+----+---------------------+-----+--+
value in the same example as above would be 9, or 'RUU=' decoded.
So we do not know the name of the rows, until the row from smv is called, once we have this, we can look up what column name we need to get the correct information.
Hope this helps.
MORE INFO
At the point of triggering, we do not know what the row consists of the right data because some many of the fields would be empty. The map is there to help we query the right column, to get the right row (smv grows over time depending on whats uploaded.)
1) SELECT p.va FROM providers p WHERE p.network = 'Recharge' ;
2) SELECT s.* FROM smv s, providers p WHERE p.network = 'Recharge';
1) gives me the correct column I need to look up and query smv, using the above examples it would come back with "value". So I need to now look up, within the smv table, C = Recharge, and value = '9'. This should bring me back row 2 of the smv table.
So individually both 1 and 2 queries work, but I need them put together so the query is done on the database server.
Hope this gives more insight
Even More Info
From reading other posts, which are not really doing what I need, i have come up with this:
SELECT s.*
FROM (SELECT
(SELECT p.va
FROM dh_smv_providers p
WHERE p.provider_name = 'vodaphone'
LIMIT 1) AS net,
(SELECT p.bex
FROM dh_smv_providers p
WHERE p.provider_name = 'vodaphone'
LIMIT 1) AS bex
FROM dh_smv_providers) AS val, dh_smv_ s
WHERE s.provider_id = 'vodaphone' AND net = '20'
ORDER BY from_base64(val.bex) DESC;
The above comes back blank, but if i replace net, in the WHERE clause with a column I know exists, I do get the results expected:
SELECT s.*
FROM (SELECT
(SELECT p.va
FROM dh_smv_providers p
WHERE p.provider_name = 'vodaphone'
LIMIT 1) AS net,
(SELECT p.bex
FROM dh_smv_providers p
WHERE p.provider_name = 'vodaphone'
LIMIT 1) AS bex
FROM dh_smv_providers) AS val, dh_smv_ s
WHERE s.provider_id = 'vodaphone' AND value = '20'
ORDER BY from_base64(val.bex) DESC;
So what I am doing wrong, which is net, not showing the value derived from the subquery "value" ?
Thanks
SELECT
v.*,
p.network, p.va
FROM
smv_ v
INNER JOIN
providers p ON p.provider_id = v.provider_id
WHERE
p.network = 'RUU=' AND p.va = 'MjU='
LIMIT 1;
The tables talk to each other via the JOIN syntax. This completely circumvents the need (and limitations) of sub-selects.
The INNER JOIN means that only fully successful matches are returned, you may need to adjust this type of join for your situation but the SQL will return a row of all v columns where p.va = MjU and p.network = RUU and p.provider_id = v.provider_id.
What I was trying to explain in comments is that subqueries do not have any knowledge of their outer query:
SELECT *
FROM a
WHERE (SELECT * FROM b WHERE a)
AND (SELECT * FROM c WHERE a OR b)
This layout (as you have in your question) is that b knows nothing about a because the b query is executed first, then the c query, then finally the a query. So your original query is looking for WHERE p.provider_id = v.provider_id but v has not yet been defined so the result is false.

SQL Query to compare two values which are in the same column but returned by two different set of queries

I have a table similar to the one shown below.
-----------------------------
JOB ID | parameter | result |
-----------------------------
1 | xyz | 10 |
1 | abc | 15 |
2 | xyz | 12 |
2 | abc | 8 |
2 | mno | 20 |
-----------------------------
I want the result as shown below.
parameter | result 1 | result 2 |
----------------------------------
xyz | 10 | 12 |
mno | NULL | 20 |
abc | 15 | 8 |
----------------------------------
My goal is to have a single table which can compare the result values of two different jobs. It can be two or more jobs.
you want to simulate a pivot table since mysql doesn't have pivots.
select
param,
max(case when id = 1 then res else null end) as 'result 1',
max(case when id = 2 then res else null end) as 'result 2'
from table
group by param
SQL FIDDLE TO PLAY WITH
If you are using MySQL there are no "outer join" need to use union right and left join:
Something like:
select t1.parameter, t1.result 'Result 1', t2.result 'Result 2' from
table as t1 left join table as t2
on t1.parameter=t2.parameter
where t1.'JOB ID' = 1 and t2.'JOB ID' = 2
union
select t1.parameter, t1.result 'Result 1', t2.result 'Result 2' from
table as t1 right join table as t2
on t1.parameter=t2.parameter
where t1.'JOB ID' = 1 and t2.'JOB ID' = 2
If the SQL with full outer join will make it more easier:
select t1.parameter, t1.result 'Result 1', t2.result 'Result 2' from
table as t1 outer join table as t2
on t1.parameter=t2.parameter
where t1.'JOB ID' = 1 and t2.'JOB ID' = 2
In Postgres, you can use something like:
select parameter, (array_agg(result))[1], (array_agg(result))[2] from my_table group by parameter;
The idea is: aggregate all the results for a given parameter into an array of results, and then fetch individual elements from those arrays.
I think that you can achieve something similar in MySQL by using GROUP_CONCAT(), although it returns a string instead of an array, so you cannot easily index it. But you can split by commas after that.
select q1.parameter, q2.result as r1, q3.result as r2
from
(select distinct parameter from temp2) q1
left join (select parameter, result from temp2 where job_id = 1) q2
on q1.parameter = q2.parameter
left join (select parameter, result from temp2 where job_id = 2) q3
on q1.parameter = q3.parameter;
It works, but it's not efficient. Still, since I'm gathering you are trying to solve something more complex than what's presented, this might help form your general solution.
While I'm at it, here's a slightly cleaner solution:
select distinct q1.parameter, q2.result as r1, q3.result as r2
from
temp2 q1
left join (select parameter, result from temp2 where job_id = 1) q2
on q1.parameter = q2.parameter
left join (select parameter, result from temp2 where job_id = 2) q3
on q1.parameter = q3.parameter;

Select all values from table B where matches table A

I have this schema:
items | taxonomy | subjects
| |
ID headline | item_id subject_id | subject_id subject
-------------------------------------------------------------------------
1 information | 1 1 | 1 cities
2 here we are | 2 1 | 2 towns
3 more things | 3 2 | 3 water
4 doo dah | 3 4 | 4 telephones
| 4 1 |
| 4 3 |
I would like to select a single row from "items" and with it, include all the rows from "subjects" which are joined by the "taxonomy" table. So for example, getting item.ID=3 would result in something like:
items.ID = 3
items.headline = "more things"
subjects.subject = "towns"
subjects.subject = "telephones"
I've started with this query
SELECT
i.ID,
i.headline,
s.subject_name
FROM items i
JOIN taxonomy t
on i.ID=t.item_id
JOIN subjects s
on t.subject_id=s.subject_id
WHERE i.ID = 3
But this only returns a single value from subject_name even if there are multiple values associated with that item_id.
EDIT
I actually had a LIMIT 1 on the query which was causing (as #Gordon Linoff said) only one row to be returned, even though there were multiple rows in the result set corresponding to the multiple subjects. His solution still does nicely, because I only want to return a single row.
Your query returns the subjects on multiple rows. If you want the subjects on a single row, then you need some form of concatenation:
SELECT i.ID, i.headline, GROUP_CONCAT(s.subject_name) as subjects
FROM items i JOIN
taxonomy t
ON i.ID = t.item_id JOIN
subjects s
ON t.subject_id = s.subject_id
WHERE i.ID = 3
GROUP BY i.ID, i.headline;
For one item, the GROUP BY is optional, but it is good form in case you modify the query to handle multiple items.
I would suggest you the "union all" clause (or "union", if you are not needing the duplicates).
(SELECT
"taxonomy" As Name,
i.headline As Value
FROM items i
JOIN taxonomy t
on i.ID=t.item_id
WHERE i.ID = 3)
Union All
(SELECT
"subject" As Name,
s.subject_name As Value
FROM items i
JOIN subjects s
on t.subject_id=s.subject_id
WHERE i.ID = 3)
You can add a 2nd field in each select to indicate type of item selected ("headline", "subjects", etc).

How to find all products with some properties?

I'm listing product properties in a MySQL table where each row contains a product ID prod and a property ID prop. If a product has three properties, this results in three rows for that product. Example table:
prod | prop
-----+-----
1 | 1
2 | 1
2 | 2
2 | 3
3 | 2
3 | 4
How can I find which products have both properties #1 and #2 (product #2)?
The only way that I can think of is one select and inner join per property, but I think that would be very inefficient. It's a search function for a website and has to work for 10k lines in the table and 10 requested properties.
SELECT prod
FROM tbl
WHERE prop IN (1, 2)
GROUP BY prod
HAVING COUNT(*) = 2
And if there will be always 2 properties to find - then INNER JOIN would be a bit more efficient:
SELECT t1.p
FROM tbl t1
INNER JOIN tbl.t2 ON t2.prod = t1.prod
AND t2.prop = 2
WHERE t1.prop = 1
The recommended index for this query to be efficient as much as possible is a compound one (prop, prod)