ORDER BY in UNION query - mysql

I seen topics explaining this but in my case it does not work.
I have query
( SELECT * FROM my_table
left join table2 on table2.id = my_table.id
left join table3 on pension.age = my_table.age
WHERE table3.id IS NULL )
UNION
( SELECT * FROM my_table
left join table2 on table2.id = my_table.id
left join table3 on pension.age = my_table.age
WHERE my_table.id FROM 75 to 245 )
ORDER BY my_table.id ASC, table2.wage DESC, table3.compensation DESC
this does not work saying user_table. or table2. or table3. not found
when i remove it its saying
ORDER BY id ASC, wage DESC, compensation DESC
this somewhat works but not desired result. please assist

Is there part of the code missing? I see no reference to table_fired. Also, aren't those curly braces used as part of an outer join in a larger query? That's why I think there's a larger part of the query missing, which might be relevant.

SELECT * FROM my_table
left join table2 on table2.id = my_table.id
left join table3 on pension.age = my_table.age
WHERE my_table.id IS NULL OR my_table.id FROM 75 to 245
ORDER BY my_table.id ASC, table2.wage DESC, table3.compensation DESC
I replaced your "table_fired" with "my_table" and combined the two subselects into one.

The union operation requires that each of your two queries have exactly the same number of columns in their result set. In mysql, UNION will always use the column names from the frist query - so if the second query uses different column names, they will be mapped by order onto the columns that were defined by the first query.
Your ORDER BY will be applied after the UNION has been run, and so it can only refer to columns that are in the result set of the UNION. These columns are not qualified by table identifiers from the constituent queries (that's why removing the table identifiers from your ORDER BY clause gets rid of the explicit errors).
Beyond that, the problem is likely that your component queries produce multiple columns that have the same name, and are distinguishable only by their table identifiers (for example my_table.id and table2.id). When you use ORDER BY id ASC ..., which of those "id" fields will be used?
Solve this problem by replacing the * with an explicit list of the relevant columns for each of the two component queries. Ensure that each column you select is given a unique name. For Example:
SELECT
my_table.id as my_table_id,
table2.id as table2_id,
table2.compensation as compensation,
table3.wage as wage
...
Your union will then pick up distinctly named columns, and your order by clause would need to refer to those instead of the table-qualified columns in the original queries:
ORDER BY my_table_id ASC, wage DESC, compensation DESC

Related

In query with joins and multi-table/field ORDER BY, how to set LIMIT offset to start from a particular row identified by a unique id field?

Suppose I have four tables: tbl1 ... tbl4. Each has a unique numerical id field. tbl1, tbl2 and tbl3 each has a foreign key field for the next table in the sequence. E.g. tbl1 has a tbl2_id foreign key field, and so on. Each table also has a field order (and other fields not relevant to the question).
It is straightforward to join all four tables to return all rows of tbl1 together with corresponding fields from the other three fields. It is also easy to order this result set by a specific ORDER BY combination of the order fields. It is also easy to return just the row that corresponds to some particular id in tbl1, e.g. WHERE tbl1.id = 7777.
QUESTION: what query most efficiently returns (e.g.) 100 rows, starting from the row corresponding to id=7777, in the order determined by the specific combination of order fields?
Using ROW_NUMBER or (an emulation of it in MySQL version < 8) to get the position of the id=7777 row, and then using that in a new version of the same query to set the offset in the LIMIT clause would be one approach. (With a read lock in between.) But can it be done in a single query?
# FIRST QUERY: get row number of result row where tbl1.id = 7777
SELECT x.row_number
FROM
(SELECT #row_number:=#row_number+1 AS row_number, tbl1.id AS id
FROM (SELECT #row_number:=0) AS t, tbl1
INNER JOIN tbl2 ON tbl2.id = tbl1.tbl2_id
INNER JOIN tbl3 ON tbl3.id = tbl2.tbl3_id
INNER JOIN tbl4 ON tbl4.id = tbl3.tbl4_id
WHERE <some conditions>
ORDER BY tbl4.order, tbl3.order, tbl2.order, tbl1.order
) AS x
WHERE id=7777;
Store the row number from the above query and use it to bind :offset in the following query.
# SECOND QUERY : Get 100 rows starting from the one with id=7777
SELECT x.field1, x.field2, <etc.>
FROM
(SELECT #row_number:=#row_number+1 AS row_number, field1, field2
FROM (SELECT #row_number:=0) AS t, tbl1
INNER JOIN tbl2 ON tbl2.id = tbl1.tbl2_id
INNER JOIN tbl3 ON tbl3.id = tbl2.tbl3_id
INNER JOIN tbl4 ON tbl4.id = tbl3.tbl4_id
WHERE <same conditions as before>
ORDER BY tbl4.order, tbl3.order, tbl2.order, tbl1.order
) AS x
LIMIT :offset, 100;
Clarify question
In the general case, you won't ask for WHERE id1 > 7777. Instead, you have a tuple of (11,22,33,44) and you want to "continue where you left off".
Two discussions, with
That is messy, but not impossible. See Iterating through a compound key . Ig gives an example of doing it with 2 columns; 4 columns coming from 4 tables is an extension of such.
A variation
Here is another discussion of such: https://dba.stackexchange.com/questions/164428/should-i-store-data-pre-ordered-rather-than-ordering-on-the-fly/164755#164755
In actually implementing such, I have found that letting the "100" (LIMIT) be flexible can be easier to think through. The idea is: reach forward 100 rows (with LIMIT 100,1). Let's say you get (111,222,333,444). If you are currently at (111, ...), then deal with id2/3/4. If it is, say, (113, ...), then do WHERE id1 < 113 and leave off any specification of id2/3/4. This means fetching less than 100 rows, but it lands you just shy of starting id1=113.
That is, it involves constructing a WHERE clause with between 1 and 4 conditions.
In all cases, your query says ORDER BY id1, id2, id3, id4. And the only use for LIMIT is in the probe to figure out how far ahead the 100th row is (with LIMIT 100,1).
I think I can dig out some old Perl code for that.

Mysql Left join index

I used the following query
select a.*,b.* from firsttable a left join secondtable b on a.id=b.item_id ORDER BY a.id DESC LIMIT 0,10
To display items from two tables, where the id of the first table is the item_id of the second. My question is , when I try to display this in php , if I want to display a.id i can try with:
while($row=$go->fetch_assoc()){
echo $row['id'];
}
or
while($row=$go->fetch_assoc()){
echo $row['a.id'];
}
since both tables have id,on the first example displays only if there are matching rows from first and second table and displays the id of second table, I want the id of first
and on the second it says undefined index.
Can you explain why is this please?
Edit:
Adding tables for example
id
info
username
id
item.id
username
Both tables have a column that has the same name, so, when using select *, it is ambiguous to which column id relates to.
The only way to remove the ambiguity is to explicitly list all the columns you want to select, using aliases for homonyms:
select
a.id,
b.id b_id, -- alias for b.id
b.item_id
-- more columns here as needed
from firsttable a
left join secondtable b on a.id=b.item_id
order by a.id desc
limit 0,10
This is one of the many reasons why select * is generally considered a bad practice in SQL.
Recommend reading: What is the reason not to use select *?

Inner Join SQL Syntax

I've never done an inner join SQL statement before, so I don't even know if this is the right thing to use, but here's my situation.
Table 1 Columns: id, course_id, unit, lesson
Table 2 Columns: id, course_id
Ultimately, I want to count the number of id's in each unit in Table 1 that are also in Table 2.
So, even though it doesn't work, maybe something like....
$sql = "SELECT table1.unit, COUNT( id ) as count, table2.id, FROM table1, table2, WHERE course_id=$im_course_id GROUP BY unit";
I'm sure the syntax of what I'm wanting to do is a complete fail. Any ideas on fixing it?
SELECT unit, COUNT( t1.id ) as count
FROM table1 as t1 inner JOIN table2 as t2
ON t1.id = t2.id
GROUP BY unit
hope this helps.
If I understand what you want (maybe you could post an example input and output?):
SELECT unit, COUNT( id ) as count
FROM table1 as t1 JOIN table2 as t2
ON t1.id = t2.id
GROUP BY unit
Okay, so there are a few things going on here. First off, commas as joins are deprecated so they may not even be supported (depending on what you are using). You should probably switch to explicitly writing inner join
Now, whenever you have any sort of join, you also need on. You need to tell sql how it should match these two tables up. The on should come right after the join, like this:
Select *
From table1 inner join table2
on table1.id = table2.id
and table1.name = table2.name
You can join on as many things as you need by using and. This means that if the primary key of one table is several columns, you can easily create a one-to-one match between tables.
Lastly, you may be having issues because of other general syntax errors in your query. A comma is used to separate different pieces of information. So in your query,
SELECT table1.unit, COUNT( id ) as count, table2.id, FROM ...
The comma at the end of the select shouldn't be there. Instead this should read
SELECT table1.unit, COUNT( id ) as count, table2.id FROM ...
This is subtle, but the sql query cannot run with the extra comma.
Another issue is with the COUNT( id ) that you have. Sql doesn't know which id to count since table1 and table2 both have ids. So, you should use either count(table1.id) or count(table2.id)

How to rewrite slow WHERE IN query

I am not very familiar with mysql and was wondering how I might rewrite the following query to fix it and speed it up. I believe I'd have to use a JOIN (or something else), but am not sure how to do this.
What I want to achieve is to perform a simple MATCH AGAINST query, and using the IDs from these results (in table2), retrieve all corresponding rows with the same IDs in table1.
SELECT *
FROM table1
WHERE id IN (
SELECT id
FROM table2
WHERE MATCH (gloss) AGAINST ('example' IN BOOLEAN MODE)
LIMIT 10
)
Note that I'm aware the subquery above doesn't work with a LIMIT.
Thank you for your time.
try a simple inner join
SELECT *
FROM table1
INNER JOIN table2
USING(id)
WHERE MATCH (gloss) AGAINST ('example' IN BOOLEAN MODE)
LIMIT 10
to run your IN with a LIMIT you have to wrap it inside another query like this
SELECT *
FROM table1
WHERE id IN (SELECT id FROM
(SELECT id
FROM table2
WHERE MATCH (gloss) AGAINST ('example' IN BOOLEAN MODE)
LIMIT 10
)T1
)
to get exact same result as your IN with LIMIT 10 using INNER JOIN you can do this
SELECT *
FROM table1
INNER JOIN
(SELECT id
FROM table2
WHERE MATCH (gloss) AGAINST ('example' IN BOOLEAN MODE)
LIMIT 10
)T1
USING (id)
Try adding the index on the id column of the table as
ALTER TABLE TABLE_NAME ADD INDEX (COLUMN_NAME);
This will definitely increase the performance of the query...
Hope this helps... :)

Create a VIEW where a record in t1 is not present in t2 ? Confirmation on Union/Left Join/Inner Join?

I am trying to make a view of records in t1 where the source id from t1 is not in t2.
Like... "what records are not present in the other table?"
Do I need to include t2 in the FROM clause? Thanks
SELECT t1.fee_source_id, t1.company_name, t1.document
FROM t1
WHERE t1.fee_source_id NOT IN (
SELECT t1.fee_source_id
FROM t1 INNER JOIN t2 ON t1.fee_source_id = t2.fee_source
)
ORDER BY t1.aif_id DESC
You're looking to effect an anti-join, for which there are three possibilities in MySQL:
Using IN:
SELECT fee_source_id, company_name, document
FROM t1
WHERE fee_source_id NOT IN (SELECT fee_source FROM t2)
ORDER BY aif_id DESC
Using EXISTS:
SELECT fee_source_id, company_name, document
FROM t1
WHERE NOT EXISTS (
SELECT * FROM t2 WHERE t2.fee_source = t1.fee_source_id LIMIT 1
)
ORDER BY aif_id DESC
Using JOIN:
SELECT t1.fee_source_id, t1.company_name, t1.document
FROM t1 LEFT JOIN t2 ON t2.fee_source = t1.fee_source_id
WHERE t2.fee_source IS NULL
ORDER BY t1.aif_id DESC
According to #Quassnoi's analysis:
Summary
MySQL can optimize all three methods to do a sort of NESTED LOOPS ANTI JOIN.
It will take each value from t_left and look it up in the index on t_right.value. In case of an index hit or an index miss, the corresponding predicate will immediately return FALSE or TRUE, respectively, and the decision to return the row from t_left or not will be made immediately without examining other rows in t_right.
However, these three methods generate three different plans which are executed by three different pieces of code. The code that executes EXISTS predicate is about 30% less efficient than those that execute index_subquery and LEFT JOIN optimized to use Not exists method.
That’s why the best way to search for missing values in MySQL is using a LEFT JOIN / IS NULL or NOT IN rather than NOT EXISTS.
However, I'm not entirely sure how this analysis reconciles with the MySQL manual section on Optimizing Subqueries with EXISTS Strategy which (to my reading) suggests that the second approach above should be more efficient than the first.
Another option below (similar to anti-join)... Great answer above though. Thanks!
SELECT D1.deptno, D1.dname
FROM dept D1
MINUS
SELECT D2.deptno, D2.dname
FROM dept D2, emp E2
WHERE D2.deptno = E2.deptno
ORDER BY 1;