Use subquery in mysql - mysql

The query below gives me 2 out of the 3 answers I'm looking for. On the sub-query select I get null instead of no
the 3 possible values for column name isCyl could be blank, yes, no
I'm not sure if the sub-query is the best way to go about it, but I don't know how else to re-state the query.
The schedule table has a series of columns to show what tasks must be completed on an assignment. Related tables store the results of the tasks if they were assigned to be completed. So I need to test if a specific task was scheduled. If so, then I need to see if the results of the task have been recorded in the related table. For brevity I am only showing one of the columns here.
SELECT s.`reckey`,
if(s.cylinders="T",
(select
if(c.areckey is not null,
"yes",
"no"
)
from cylinders c where c.areckey = s.reckey limit 1
)
,""
) as isCyl
from schedule s
where s.assignmentDate between 20161015 and 20161016
order by s.reckey

Use a LEFT JOIN, which returns NULL for columns in the child table when there's no match.
SELECT s.reckey, IF(s.cylinders = "T",
IF(c.areckey IS NOT NULL, 'yes', 'no'),
"") AS isCyl
FROM schedule AS s
LEFT JOIN cylinders AS c ON c.areckey = s.reckey
WHERE s.assignmentDate between 20161015 and 20161016
ORDER BY s.reckey
If there can be multiple rows in cylinders with the same areckey, change it to:
LEFT JOIN (select distinct areckey FROM cylinders) AS c on c.areckey = s.reckey
or use SELECT DISTINCT in the main query.

Related

SQL Selecting count from join returning 0 instead of null result

I have a query that gets data and also joins another table (A) and counts the rows in that join table (B). However if the main table (A) is empty I want the query to return nothing. However it is returning a result of null for id and date and an integer value of 0 for users instead of a null row. How do I get an empty result instead of it returning something?
Returning:
id | date | users
null | null | 0
SQL Code
SELECT
`sessions`.`id`,
`sessions`.`date`,
COUNT( sessions_users.id ) AS users
FROM
`sessions`
LEFT JOIN `sessions_users` ON `sessions`.`id` = `sessions_users`.`sessions_id`
An aggregate query without a group by clause always returns a single record, regardless of the content of the underlying result set (and even if it is empty).
But, since you have non-aggregated columns in the select clause (sessions.id and sessions.date), your query is missing a group by clause anyway. In non-ancient versions in MySQL, where sql mode ONLY_FULL_GROUP_BY is enabled by default, this is a syntax error.
Consider:
SELECT
`sessions`.`id`,
`sessions`.`date`,
COUNT( sessions_users.id ) AS users
FROM
`sessions`
LEFT JOIN `sessions_users` ON `sessions`.`id` = `sessions_users`.`sessions_id`
GROUP BY
`sessions`.`id`,
`sessions`.`date`
This will produce one record per session id and date, along with the count of matching records in sessions_users. If there are no records in sessions, the query will return an empty result set.
If I understand correctly, instead of NULL, you want something like this:
id | date | users
| | 0
If so, just simply use IFNULL() in your SELECT as such:
SELECT
IFNULL(`sessions`.`id`,' ') as id,
IFNULL(`sessions`.`date`,' ') as date,
....
There are also a few other ways to achieve this using just IF() or CASE .. but IFNULL is very straight forward.
BUT if you don't want to see any NULL and 0 values, change your LEFT JOIN to INNER JOIN and you're done.
From your description, it sounds like you want an inner join:
SELECT s.id, s.date, COUNT(*) as users
FROM sessions s JOIN
sessions_users su
ON su.id = su.sessions_id;

return values of table 1 based on single column in table 2

I have 3 tables that I am using and need to make a query to return data from one table based on the value of a single column in the second table.
tbl_user
ID
login
pass
active
mscID
tbl_master
ID
name
training_date
MSCUnit
Active
tbl_msc
mscID
mscName
my current SQL statement:
SELECT
tbl_master.ID,
tbl_master.name,
tbl_master.training_date,
tbl_master.MSCUnit,
tbl_master.active,
tbl_user.mscID
FROM
tbl_master,
tbl_user
WHERE
tbl_master.active = 1 AND tbl_master.MSCUnit = tbl_user.mscID
The values stored in tbl_msc.mscID is a varchar(11) and it contains a string similar to A00 or A19. This is also the Primary key in the table.
The values stored in tbl_user.mscID matches that of tbl_msc.mscID. The values stored in tbl_master.UnitMSC also matches that of tbl_msc.mscID.
My goal is to return all records from tbl_master where the currently logged in user has the same mscID. The problem I am having is the statement returns all records in tbl_master.
I have tried several different join statements and for some reason, I cannot get this to filter correctly.
I am missing something. Any assistance in the SQL statement would be appreciated.
Thanks,
Will
You should be writing this using joins. I don't know how you know who the current user is, but the idea is to join the three tables together:
SELECT m.ID, m.name, m.training_date, m.MSCUnit, m.active,
u.mscID
FROM tbl_master m JOIN
tbl_user u
ON m.MSCUnit = u.mscID JOIN
tbl_msc msc
ON msc.mscID = u.msc_ID
WHERE m.active = 1 AND msc.mscName = ?;
Notice the use of proper, explicit, standard JOIN syntax and table aliases.
Select a.*, b.userid from
table_master a, table_user b where
a.mscunit in (select mscid from
table_user where active=1)
This should point you in the right direction.

Complicated MySql Update Join Query

Have is an example of the problem I'm facing. The database tables are a little different than usual, but needed to be setup this way.
Items: id, order_id, other fields
Items_Drinks: id, drinks, other fields
Orders: id, other fields
Orders_Drinks: id, drinks, other fields
I need to have an update query that will update the Orders_Drinks table with the sum of the Items_Drinks drinks field that have the same order_id as Orders_Drinks id field.
Items: 1 1 ...
Items: 2 1 ...
Items_Drinks: 1 4 ...
Items_Drinks: 2 5 ...
Orders: 1 ...
Orders_Drinks: 1 9 ...
The Orders_Drinks is currently correct, but if I were to update Items_Drinks with id of 1 to 5, I would need an update command to get Orders_Drinks with id 1 to equal 10.
It would be best if the command would update every record of the Orders_Drinks.
I know my database is not typical, but it is needed for my application. This is because the Drinks table is not needed for all entries. The Drinks table has over 5000 fields in it, so if every record had these details the database would grow and slow for no real reason. Please do not tell me to restructure the database, this is needed.
I am currently using for loops in my C# program to do what I need, but having 1 command would save a ton of time!
Here is my best attempt, but it gives an error of "invalid group function".
update Orders_Drinks join Items on Items.order_id=Orders_Drinks.id join Items_Drinks on Items_Drinks.id=Items.id set Orders_Drinks.drinks=sum(Item_Drinks.drinks);
I think this is what you're wanting.
Edited:
UPDATE `Order_Drinks` a
SET a.`drinks` = (SELECT SUM(b.`drinks`) FROM `Items_Drinks` b INNER JOIN `Items` c ON (b.`id` = c.`id`) WHERE a.`id` = c.`order_id`)
That should give you a total of 9 for the Order_Drinks table for the row id of 1.
This is assuming that Orders.id == Orders_Drinks.id and that Items.id == Items_Drinks.id.
You need to do an aggregation. You can do this in the join part of the update statement:
update Orders_Drinks od join
(select i.order_id, sum(id.drinks) as sumdrinks
from Items i join
Items_Drinks id
on id.id = i.id
) iid
on iid.order_id = od.id
set od.drinks = iid.sumdrinks;
Something like this will return the id from the orders_drinks table, along with the current value of the drinks summary field, and a new summary value derived from the related items_drinks tables.
(Absent the name of the foreign key column, I've assumed the foreign key column names are of the pattern: "referenced_table_id" )
SELECT od.id
, od.drinks AS old_drinks
, IFNULL(td.tot_drinks,0) AS new_drinks
FROM orders_drinks od
LEFT
JOIN ( SELECT di.orders_drinks_id
, SUM(di.drinks) AS tot_drinks
FROM items_drinks di
GROUP BY di.orders_drinks_id
) td
ON td.orders_drinks_id = od.id
Once we have SELECT query written that gets the result we want, we can change it into an UPDATE statement. Just replace SELECT ... FROM with the UPDATE keyword, and add a SET clause, to assign/replace the value to the drinks column.
e.g.
UPDATE orders_drinks od
LEFT
JOIN ( SELECT di.orders_drinks_id
, SUM(di.drinks) AS tot_drinks
FROM items_drinks di
GROUP BY di.orders_drinks_id
) td
ON td.orders_drinks_id = od.id
SET od.drinks = IFNULL(td.tot_drinks,0)
(NOTE: the IFNULL function is optional. I just used it to substitute a value of zero whenever there are no matching rows in items_drinks found, or whenever the total is NULL.)
This will update all rows (that need to be updated) in the orders_drinks table. A WHERE clause could be added (after the SET clause), if you only wanted to update particular rows in orders_drinks, rather than all rows:
WHERE od.id = 1
Again, to get to this, first get a SELECT statement working to return the new value to be assigned to the column, along with the key of the table to be updated. Once that is working, convert it into an UPDATE statement, moving the expression that returns the new value down to a SET clause.

MYSQL how to speed up requests using EXISTS

i have an SQL Requests:
SELECT DISTINCT id_tr
FROM planning_requests a
WHERE EXISTS(
SELECT 1 FROM planning_requests b
WHERE a.id_tr = b.id_tr
AND trainer IS NOT NULL
AND trainer != 'FREE'
)
AND EXISTS(
SELECT 1 FROM planning_requests c
WHERE a.id_tr = c.id_tr
AND trainer IS NULL
)
but this requests take 168.9490 sec to execute for returning 23162 rows of 2545088 rows
should i use LEFT JOIN or NOT IN ? and how can i rewrite it thx
You can speed this up by adding indexes. I would suggest: planning_requests(id_tr, trainer).
You can do this as:
create index planning_requests_id_trainer on planning_requests(id_tr, trainer);
Also, I think you are missing an = in the first subquery.
EDIT:
If you have a lot of duplicate values of id_tr, then in addition to the above indexes, it might make sense to phrase the query as:
select id_tr
from (select distinct id_tr
from planning_requests
) a
where . . .
The where conditions are being run on every row of the original table. The distinct is processed after the where.
I think your query can be simplified to this:
SELECT DISTINCT a.id_tr
FROM planning_requests a
JOIN planning_requests b
ON b.id_tr = a.id_tr
AND b.trainer IS NULL
WHERE a.trainer < 'FREE'
If you index planning_requests(trainer), then MySQL can utilize an index range to get all the rows that aren't FREE or NULL. All numeric strings will meet the < 'FREE' criteria, and it also won't return NULL values.
Then, use JOIN to make sure each record from that much smaller result set has a matching NULL record.
For the JOIN, index planning_requests(id_tr, trainer).
It might be simpler if you don't mix types in a column like FREE and 1.

MySQL query was working before, now it isn't?

I'm having an odd problem, and I don't have the slightest idea of why it isn't working.
I have the following query that I constructed:
SELECT servers.id, servers.name, servers.address, servers.port, servers.up, servers.down, servers.genre, servers.score, servers.version, servers.country, ROUND( AVG( reviews.average ) , 0 ) AS review
FROM servers
INNER JOIN reviews ON servers.id = reviews.server
ORDER BY servers.score DESC
This query was working fine a few weeks ago. It is meant to get many fields from the "servers" table, and the average field from the "reviews" table where the server in the "reviews" table is the same as the id in the "servers" table.
Like I said, this query was working fine before. Yesterday I noticed that a vital part of my site wasn't working, and I figured out that this query is failing.
I've confirmed that is returning exactly 1 row (when, at the moment, it should be returning 4, because there are 4 entries in the "servers" table.)
This is what phpMyAdmin gives me when I execute that query:
id name address port up down genre score version country review
NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
Could anybody enlighten me? I've come here as a last resort, because I am stuck.
As mentioned in the comments, try changing the INNER JOIN to a LEFT OUTER JOIN which will return servers, regardless if there is a matched row in the review table or not. Also, you didn't post your schema, but double check the reviews.server column in the reviews table, it may be server_id instead. Another issue, you are doing an AVG which is a grouped calculation, but you have no GROUP BY clause, so I would suggest adding it, so your full query should look like:
SELECT servers.id, servers.name, servers.address, servers.port, servers.up, servers.down, servers.genre, servers.score, servers.version, servers.country, ROUND( AVG( reviews.average ) , 0 ) AS review
FROM servers
LEFT OUTER JOIN reviews ON servers.id = reviews.server # might be reviews.server_id
GROUP BY reviews.server
ORDER BY servers.score DESC
More info about GROUP BY functions.
-- Update --
SELECT servers.id, servers.name, servers.address, servers.port, servers.up, servers.down, servers.genre, servers.score, servers.version, servers.country, IFNULL(ROUND(AVG(reviews.average)), 0) AS review
FROM servers
LEFT OUTER JOIN reviews ON servers.id = reviews.server
GROUP BY servers.id
ORDER BY servers.score DESC