mysql join issue when one of the values is not assigned - mysql

I have the issue of getting the records out of database in the specific condintion. I have table 'test' I want to get the listing from sorted by driverid and table 'drivers' which i use to adjust sorting of the listing from 'test' table.
My query:
SELECT * FROM test JOIN drivers ON test.driverid=drivers.driverid ORDER BY queno
Table 'drivers' looks like:
driver | driverid | queno
-------------------
drv1 | 15 | 3
drv2 | 30 | 1
drv3 | 40 | 2
Problem is when there is no value assigned to 'driverid' in 'test' table then these results are listed at very beginning. I would like to have these listed at the end
How to achieve that? Thx in advance!

You can make driver.driverid primary key (PK) and test.driverid foreign key (FK) and enforce data integrity. This will also eliminate your problem.

Place a minus sign (-) before the column name and switch the ASC to DESC or DESC to ASC order (opposite to what you want).
try this:-
SELECT * FROM test JOIN drivers ON test.driverid=drivers.driverid ORDER BY -queno DESC;
Note:- While this may work well for numbers and dates, it may not be the best solution to sort fields with alpha or alphanumeric values

I found the working solution elswhere:
MySQL: Order by field, placing empty cells at end
SELECT * FROM test JOIN drivers ON test.driverid=drivers.driverid ORDER BY if(queno = '' or queno is null,1,0), queno

Related

Can't run specific SQL query on my database

I have a database for a chat application.
CREATE TABLE Users (uid int PRIMARY KEY, name text, phone text );
CREATE TABLE Messages (recipient int REFERENCES Users(uid), sender int
REFERENCES Users(uid), time timestamp NOT NULL, message text NOT NULL,
PRIMARY KEY (recipient, sender, time));
http://www.sqlfiddle.com/#!9/bd36d1
I want to define, for each of the 5 users which have sent the most messages, the average length of messages that have been sent by this user.
I have written the following query:
SELECT avg(strlen(message))
FROM Messages
WHERE sender IN
(SELECT *
FROM (SELECT sender, COUNT(sender) AS NumberOfMessages
FROM Messages
GROUP BY sender) AS MessagesPerSender
ORDER BY NumberOfMessages DESC
LIMIT 5)
To start with, is this query correct? Does it give me the desired result? The problem is I can't run it at all cause I get the error:
"This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery"
Not the right approach for mysql this may do
select sender,avg(length(message)),count(*)
from messages
group by sender
order by avg(length(message)) desc limit 5;
+--------+----------------------+----------+
| sender | avg(length(message)) | count(*) |
+--------+----------------------+----------+
| 1 | 9.0000 | 1 |
| 9 | 5.5000 | 2 |
| 2 | 5.0000 | 1 |
+--------+----------------------+----------+
3 rows in set (0.00 sec)
Note this may not deal with draws in the way you want.
You had 2 errors in your code:
first of all you cannot use strlen in MYSQL. That is an Microsoft
SQL Server dialect Instead you need to use length.
Secondly, in the subquery you used, you were using two columns
instead on one. This will cause the query to fail because the equals
operator needs to be equal to the value in only one column.
So here is your query:
select u.name, avg(length(m.message)), count(*)
from Messages m
inner join Users u on m.sender = u.uid
group by u.name
order by avg(length(m.message)) desc limit 5;
I improved on P. Salmon's answer since I provided you with the name of the sender rather than their ID.
Hope this helps :)
To find out, I have changed the DMBS from MySQL to Postgres, which supports inner limit. Your query has correct syntax, except the strlen() function, the correct one is length().
However, your query fails for a simple reason: you are doing a where sender in (subquery), although your subquery returns two fields. The in operator only works with single field queries. Moreover, your subquery is composed of two queries, which can be simplified to one. The following query works on Postgres 9.6, and should work on whatever version of MySQL with inner limit support:
SELECT avg(length(message))
FROM Messages
WHERE sender IN (
SELECT sender
FROM Messages
GROUP BY sender
ORDER BY COUNT(sender) DESC
LIMIT 5
)
It produces the following result when run on your sample data:
+----------+
| avg |
+----------+
| 6.25 |
+----------+
Working SQL Fiddle (Postgres 9.6): http://www.sqlfiddle.com/#!17/bd36d/6/0

MySQL 5.7 return all columns of table based on distinct column

I just upgraded to MySQL 5.7 and unfortunately for me, some of the functionality of GROUP BY is gone. I wanted to select all movies from my movies table with as long as the movies.id of type int is not a duplicate. My previous query in MySQL 5.6 was:
SELECT *
FROM movies
WHERE movies.title LIKE '%example%'
GROUP BY movies.id
If I had two movies with the same id, it would only display one movie, instead of that movie and its duplicates.
When I upgraded to MySQL 5.7, the GROUP BY gave me errors and I was instead told to use ORDER BY. However this query:
SELECT *
FROM movies
WHERE movies.title LIKE '%example%'
ORDER BY movies.id
Does return duplicate movies. So, is there a way to filter this out, and only return a row if it isn't a duplicate?
Edit: For example if this is my movies table:
movies
==================
| id | title |
==================
| 1 | example |
------------------
| 2 | example |
------------------
| 1 | example |
------------------
Here is the output of each query:
Previous query result (with MySQL 5.6)
=======
1 | example
2 | example
New query result (with MySQL 5.7 and ORDER BY)
=======
1 | example
1 | example
2 | example
I want the final result to contain no duplicates (so the result should look like the first query result).
Edit 2: I understand I was sort of abusing the way MySQL handled GROUP BY. Unfortunately, I do not have much experience with MySQL and got that answer from StackOverflow. I would just like to return all columns in my table that do not contain duplicate ids.
I believe would be easy to use distinct keyword
SELECT distinct movies.*
FROM movies
WHERE movies.title = 'example'

HowTo: Query MySQL to retrieve search data, while limiting the results and sorting by a field.

I have two simple Mysql tables:
SYMBOL
| id | symbol |
(INT(primary) - varchar)
PRICE
| id | id_symbol | date | price |
(INT(primary), INT(index), date, double)
I have to pass two symbols to get something like:
DATE A B
2001-01-01 | 100.25 | 25.26
2001-01-02 | 100.23 | 25.25
2001-01-03 | 100.24 | 25.24
2001-01-04 | 100.25 | 25.26
2001-01-05 | 100.26 | 25.28
2001-01-06 | 100.27 | 30.29
Where A and B are the symbols i need to search and the date is the date of the prices. (because i need the same date to compare symbol)
If one symbol doesn't have a date that has the other I have to jump it. I only need to retrive the last N prices of those symbols.
ORDER: from the earliest date to latest (example the last 100 prices of both)
How could I implement this query?
Thank you
Implementing these steps should bring you the desired result:
Get dates and prices for symbol A. (Inner join PRICE with SYMBOL to obtain the necessary rows.)
Similarly get dates and prices for symbol B.
Inner join the two result sets on the date column and pull the price from the first result set as the A column and the other one as B.
This should be simple if you know how to join tables.
I think you should update your question to resolve any of the mistakes you made in representing your data. I'm having a hard time following the details. However, I think based on what I am seeing there are four MySQL concepts you need to solve your problem.
The first is JOINS you would use a join to put two tables together so you may select related data using the key that you describe as "id_symbol"
The second would be to use LIMIT which will allow you to specify the number of records to return such as that if you wanted one record you would use the keywould LIMIT 1 or if you wanted a hundred records LIMIT 100
The third would be to use a WHERE clause to allow you to search for a specific value in one of your fields from the table you are querying.
The last is the ORDER BY which will allow you to specify a field to sort your returned records and the direction you want them sorted ASC or DESC
An Example:
SELECT *
FROM table1
JOIN table2 ON table1.id = table2.table1_id
WHERE table1.searchfield = 'search string'
LIMIT 100
ORDER BY table1.orderfield DESC
(This is pseudo code so this query may not actually work but is close and should provide you with the correct idea.)
I suggest referencing the MySQL documentation found here it should provide everything you need to keep going.

Update subsequent duplicate field values in mysql

I have the following schema:
id | order_ref | description | price
Currently I have the following duplicate issue:
1 | 34567 | This is the description | 19.99
2 | 34567 | This is the description | 13.99
This was due to the data I was importing having the description for each item duplicated. Is there a way I can keep the first row, and then UPDATE the description on subsequent (up to approx 20 rows) to be 'AS ABOVE'?
1 | 34567 | This is the description | 19.99
2 | 34567 | - AS ABOVE - | 13.99
Thanks
-------UPDATED
UPDATE documents_orders_breakdown
SET `desc` = '- AS ABOVE -'
WHERE NOT id IN (SELECT id
FROM documents_orders_breakdown AS D
WHERE D.`desc` <> `desc`
ORDER BY D.id
LIMIT 1)
But this returns [Err] 1235 - This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
--------UPDATED
UPDATE documents_orders_breakdown
SET `desc` = '- AS ABOVE -'
WHERE NOT id IN (SELECT MIN(id)
FROM documents_orders_breakdown AS t
WHERE t.`desc` = `desc`)
This now returns [Err] 1093 - You can't specify target table 'documents_orders_breakdown' for update in FROM clause
If this is a one-time thing, performance is not a big issue. You can run an UPDATE on all the records that are not returned by a SELECT with a LIMIT of 1.
UPDATE the_table
SET description = '- AS ABOVE -'
WHERE NOT id IN (SELECT id
FROM the_table t
WHERE t.description = the_table.description
ORDER BY t.id
LIMIT 1)
This query assumes you want to keep the description of the record whose id comes first (hence the ORDER BY).
Since you can't use LIMIT in subqueries, you can work around that by using the aggregate function MIN:
UPDATE the_table
SET description = '- AS ABOVE -'
WHERE NOT id IN (SELECT MIN(id)
FROM the_table t
WHERE t.description = the_table.description)
(Let's hope you can mix MIN and subqueries ;)
Apparently you can't SELECT from the table you're UPDATEing in MySQL. A workaround is to use an implicit temporary table. This is bad for performance, but, again, given this is a one-time thing, that's not a big concern.
UPDATE the_table
SET description = '- AS ABOVE -'
WHERE NOT id IN (SELECT m FROM (SELECT MIN(id) AS m
FROM the_table t
WHERE t.description = the_table.description) AS temp)
Relational datebases do not have a notion of subsequent. Records in a table are not in any particular order. If you do not specify an order in a SELECT query, you have to assume that the records are retrieved in an order that you do not expect.
The comment Oswald made about ordering (or lack thereof) of the rows is very important. You have no garuntee, period, that unsorted rows selected out of this table will be in the order you expect. This means that unless you specify the existing in table order every single time, things could be tagged 'AS ABOVE' even when this does not reflect reality. In addition, none of the provided solutions so far will deal with any out-of-sequence records properly.
Overall, this sounds more like a database design issue (specifically, a normalization problem), than a query issue.
Ideally, the descriptions would be extracted to some master datatable (along with the necessary ids). Then, the choice about the description to use is left to when the 'SELECT' runs. This has the added benefit of making the 'AS ABOVE' safe for changes in ordering.
So, assuming that each instance of the order_ref column should have a different description (barring the 'AS ABOVE' bit), the tables can be refactored as followed:
id | order_ref | price
=======================
1 | 34567 | 19.99
2 | 34567 | 13.99
and
order_ref_fk | description
==========================================
34567 | "This is the description"
At this point, you join to the description table normally. Displaying a different description is usually a display issue regardless, to be handled by whatever program you have outputting the rows to display (not directly in the database).
If you insist on doing this in-db, you could write the SELECT in this vein:
SELECT Orders.id, Orders.order_ref, Orders.price,
COALESCE(Dsc.description, 'AS ABOVE')
FROM Orders
LEFT JOIN (Description
JOIN (SELECT order_ref, MIN(id) AS id
FROM Orders
GROUP BY order_ref) Ord
ON Ord.order_ref = Description.order_ref_fk) Dsc
ON Dsc.order_ref_fk = Orders.order_ref
AND Dsc.id = Orders.id
ORDER BY Orders.order_ref, Orders.id

How do I optimize this MySQL query?

I've got a really big problem, and it stems from a table with 50k+ records.
This table looks something like this (+15 or so more columns that aren't too important):
table_1
date | name | email | num_x | num_y
I also have another table ON A DIFFERENT DB (same server) that looks something like this (+1 not important column):
table_2
name | comment | status
table_1 is updated daily with new entries (it is a feed table for use on other projects), which means there are a lot of repeat "name" rows. This is intended. table_2 contains comments and status notes about "name"s, but no repeat "name"s.
I need to write a query that will select all "name"s from table_1 where the total of all num_x + num_y > X. So, for example, if this were a few rows...
2010-11-19 | john.smith | john.smith#example.com | 20 | 20
2010-11-19 | joel.schmo | joel.schmo#example.com | 10 | 10
2010-11-18 | john.smith | john.smith#example.com | 20 | 20
2010-11-18 | joel.schmo | joel.schmo#example.com | 10 | 10
.. and I needed to find all "name"s with total num_x + num_y > 50, then I'd return
john.smith | john.smith#example.com | 80 . I would also return john.smith's status and comment from the other DB.
I wrote a query that I believe works fine, but it's problematic because it takes forever and a day to run. I also successfully retrieve records from the other db (I don't have that listed below).
SELECT
name,
email,
SUM(num_x + num_y) AS total
FROM
table_1
GROUP BY
name
HAVING
SUM(num_x + num_y) > 100
ORDER BY
total ASC
Is there a better way to go about this?
Thanks everyone!
Dylan
Why do you repeat the sum in GHAVING rather than repeat total? Unless im missing something, there is no difference in results and avoiding the second sum would save time
If you can skip the ORDER BY clause and don't mind the slightly different select, I think you'll get some amount of speed up by splitting up the sum. I have a small database and have tested that its a valid query and results are correct, but its not nearly large enough to quantify the performance difference.
SELECT
name,
email,
SUM(num_x) as sumX, SUM(num_y) AS sumY
FROM
table_1
GROUP BY
name
HAVING
sumX + sumY > 100
An index on name is a no-brainer. That's the simplest thing that will speed it up.
Create an index for name, this will improve the performance:
ALTER TABLE `table_1` ADD INDEX (`name`);
But, redesigning your databases would be my recomendation. Create an artificial key for names, something like id_name | name | email, beeing id_name an integer auto_increment, this way you'll have a better performance.
Try:
SELECT
name,
email,
num_x + num_y AS total
FROM
table_1
WHERE
num_x + num_y > 100
ORDER BY
total ASC
Just getting rid of the grouping should make quite a significant difference.
maybe change the database the sum is made everytime you change x or y but it really depends of how often you change them...
Otherwise you can try to do the sum only once...
but I don't see why you do a order by on only one table if you've got a primary key...