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
Related
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)
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.
I've written a query that builds a small table of information from a couple of data sources, it uses a self made table to reference the vehicle model for the final group by which is how the data needs to be viewed, however when I group by vehicle it misses out figures in the subquery column from the group by, i.e. if I group by Prefix it shows the correct numbers, grouped by Vehicle hides off some of the data.
The Prefix can relate to a couple of like vehicle models and hence the need to group by vehicle. Can anyone see what I've done wrong easily from the SQL query below please.
SELECT Vehicle, COUNT(`Chassis-No`) AS Stock,
ROUND((100/COUNT(`Chassis-No`)) * SUM(CASE WHEN `Vehicle Age` > '182' THEN 1 ELSE 0 END),1) AS Perc6Months,
ROUND((100/COUNT(`Chassis-No`)) * SUM(CASE WHEN `Vehicle Age` > '365' THEN 1 ELSE 0 END),1) AS Perc12Months,
(SELECT COUNT(VIN_Prefix) FROM Orderdownload
INNER JOIN VehicleMatrix ON (`VIN_Prefix` LIKE 'S%' AND Prefix = LEFT(`VIN_Prefix`,2)) OR (`VIN_Prefix` NOT LIKE 'S%' AND Prefix = LEFT(`VIN_Prefix`,1)) WHERE DealerCode = 'AA12345' AND `VIN_Prefix` = IF(LEFT(`Chassis-No`,1)='S',LEFT(`Chassis-No`,2),LEFT(`Chassis-No`,1))) As Qty
FROM DealerAgedStock
INNER JOIN VehicleMatrix AS VM
ON (`Chassis-No` LIKE 'S%' AND Prefix = LEFT(`Chassis-No`,2)) OR (`Chassis-No` NOT LIKE 'S%' AND Prefix = LEFT(`Chassis-No`,1))
WHERE `DL Dealer Code` = 'AA12345'
GROUP BY Vehicle
Grouped on Vehicle I get the following:
Vehicle | Perc6Months | Perc12Months | Qty
Mondeo | 37.5 | 0 | 2
Grouped on Prefix I get the following:
VIN_Prefix | Perc6Months | Perc12Months | Qty
S1 | 25 | 0 | 2
S2 | 50 | 0 | 2
Ideally it should look this this:
Vehicle | Perc6Months | Perc12Months | Qty
Mondeo | 37.5 | 0 | 4
Where S1 and S2 are relative to the Vehicle Mondeo, thus it gives me the first instance of subquery rather than adding them together.
My question is: why does the Group By not add the figures together properly from the subquery? I need it to add them to have the correct figures...
i have two tables
activity
id | user_id | time | activity_id
1 | 1 | | 3
2 | 1 | | 1
and preferences
user_id | running | cycling | driving
1 | TRUE | FALSE | FALSE
i need result set of
id | user_id | time |
2 | 1 | |
i only need rows from first table whose values are set true in preferences table.
e.g activity_id for running is 1 which is set true in preferences table, so it returns while others doesn't.
If you can edit the schema, it would be better like this:
activity
id | name
1 | running
2 | cycling
3 | driving
user_activity
id | user_id | time | activity_id
1 | 1 | | 3
2 | 1 | | 1
preferences
user_id | activity_id
1 | 1
A row in preferences indicates a TRUE value from your schema. No row indicates a FALSE.
Then your query would simply be:
SELECT ua.id, ua.user_id, ua.time
FROM user_activity ua
JOIN preferences p ON ua.user_id = p.user_id
AND ua.activity_id = p.activity_id
If you want to see the activity name in the results:
SELECT ua.id, ua.user_id, ua.time, activity.name
FROM user_activity ua
JOIN preferences p ON ua.user_id = p.user_id
AND ua.activity_id = p.activity_id
JOIN activity ON ua.activity_id = activity.id
What I would probably do is join the tables on a common column, looks like user_id is a common column in this case, which gives access to the columns in both tables to query against in the where clause of the query.
Which type of join depends on what information you want from preferences
Handy Visual Guide for joins
So you could query
SELECT * FROM activity LEFT JOIN preferences ON activity.user_id = preferences.user_id WHERE preferences.columnIWantToBeTrue = true
I'm using left join since you mentioned you want the values from the first table based on the second table.
Mike B has the right answer. The relational model relates rows together by common values.
You've got a table named activity with an id column which looks like the primary key. The column name activity_id would typically be the name of a column in another table that is a foreign key to the activity table, referencing activity.id.
It looks like you've used the activity_id column in the activity table as a reference to either "running", "cycling" or "driving".
It's possible to match activity.activity_id = 1 with "running", but this is a bizarre design.
Here's an example query:
SELECT a.id
, a.user_id
, a.time
FROM activity a
JOIN preferences p
ON p.user_id = a.user_id
AND ( ( p.running = 'TRUE' AND a.activity_id = 1 )
OR ( p.cycling = 'TRUE' AND a.activity_id = 2 )
OR ( p.driving = 'TRUE' AND a.activity_id = 3 )
)
But, again, this is a bizarre design.
As a start, each table in your database should have rows that represent either an entity (a person, place, thing, concept or event that can be uniquely identified, is important, and we need to store information about), or a relationship between the entities.
From the limited information we have about your use case, the entities appear to be "user", an "activity_type" (running, cycling, driving), an "activity" (an amount of time, for a user and an activity_type) and some user "preference" about which activity_types the user prefers.
See the answer from Mark B for a possible schema design.
I currently have a web app where users can share posts and connect with other users. I store each post in a table named "posts" and all of the relationships between users in a table called "relations"
The structure of "posts" is:
+-------------+-----------+---------+------------+----------------+
| post_id | user_id | text | date | privacy |
+-------------+-----------+---------+------------+----------------+
| 1 | 4 | Hello | 1/13/2014 | 2 |
+-------------+-----------+---------+------------+----------------+
Privacy can either be 1 or 2
The structure of "relations" is:
+-------------+-----------+------------+------------+
|rel_id | sender | recipient | status |
+-------------+-----------+------------+------------+
| 1 | 17 | 4 | 1 |
+-------------+-----------+------------+------------+
Status can either be 1 or 2
Now, I want to have a "News Feed" like page where the user can see all of the posts from the people they are either friends with (status= 2) or following (status= 1). But I am having trouble with the query. I know how to do simple select queries, but I don't think that is possible with this.
So, I would like to select all of the posts from the "posts" table where the 'user_id' is the same as 'recipient' in the "relations" table where also the sender equals '17' (I am going to use a variable). Now on top of that, if the status of that row from "relations" is '1' and the 'privacy' from the "posts" row is '2', then skip that post.
How would I write this query?
Use joins
SELECT * FROM `posts`
join `relations` on `recipient` = `user_id`
WHERE `status` = 2
Use joins and where clauses, as follows:
SELECT *
FROM posts p
JOIN relations r ON p.user_id = r.recipient
WHERE (r.status = 1 OR r.status = 2)
AND (r.status != 1 OR p.privacy != 2);
For succinctness, it helps to alias the tables (eg "posts p") so that you can subsequently refer to fields from each of them specifically (eg "p.privacy").
This will join the tables, including any where relations.status is 1 or 2, yet skipping any where both relations.status is 1 and posts.privacy is 2.