mysql union wrong columns in result - mysql

I am getting wrong column names when using union.
Here is what i do, i have two very big tables with same structure and different records, so here it is.
mysql> select * from e18 where `15` like '%car%' limit 1;
+------+------+----+------+------+------+------+------+------+------+------+------+------+------+------+-------------+------+------+------+------+------+------+--------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+-----------+
| id | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
+------+------+----+------+------+------+------+------+------+------+------+------+------+------+------+-------------+------+------+------+------+------+------+--------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+-----------+
| 2730 | 2730 | 18 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | cars: stuff | NULL | NULL | NULL | NULL | NULL | NULL | 5 1 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | yy |
+------+------+----+------+------+------+------+------+------+------+------+------+------+------+------+-------------+------+------+------+------+------+------+------+--------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+-----------+
1 row in set
mysql> (select * from e8 where `15` like '%car%') union
(select * from e10 where `15` like '%car%') union
(select * from e18 where `15` like '%car%') limit 1;");
+------+------+----+------+------+------+------+------+------+------+------+------+------+------+------+------+-------------+------+------+------+------+------+------+------+--------+------+------+------+------+------+------+------+------+------+------+------+------+------+-----------+
| id | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 15 |
+------+------+----+------+------+------+------+------+------+------+------+------+------+------+------+------+-------------+------+------+------+------+------+------+------+--------+------+------+------+------+------+------+------+------+------+------+------+------+------+-----------+
| 2730 | 2730 | 18 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | cars: stuff | NULL | NULL | NULL | NULL | NULL | NULL | NULL | 5 1 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | yy |
+------+------+----+------+------+------+------+------+------+------+------+------+------+------+------+------+-------------+------+------+------+------+------+------+------+--------+------+------+------+------+------+------+------+------+------+------+------+------+------+-----------+
1 row in set
Union all and union return same result in this case.
There is only one row with word part "car" in it and it is in table e18.
For some reason, column names in result that i get from using usion are messed up, looks like i am missing something, any ideas what it is ?
Thanks in advance.

Union works by column position NOT name. But you have not specified the column position because you did * so it's in some order picked by the database, but not picked by you.
The name of the final result set is the name of the columns in the first query in the union.
The fix is easy: Write out the names of all the columns you want, and make sure to keep the order consistent between all three queries.
The columns are NOT sorted by name (so renaming the columns will not help you), the order is some internal order in the database.
Using * is considered poor practice: You don't know what you are getting, and if you only need some of the columns then using * retrieves more data then necessary, making things slower.
BTW Naming columns like this (by number) is very poor programming practice. How in the world do you keep things straight? Your columns have numbers, your tables have numbers. Are you trying to write obfuscated code? To make sure no one else can ever work on your code? Because if you are, this is one way to do it.

It appears that in the first query your field 15 is in numerical order with the other fields. In the 2nd query, it's showing up at the end. If you specify the fields you want (yes I know it's a lot of typing), then you won't have this problem.
Secondarily, given the number of NULLs in your return set and the fact that you're using multiple tables to store the same sort of data, your data probably isn't well normalized. Your database will be much easier to use (as well as faster and more efficient) if you normalize it.
And thirdly, 15 is not a reasonable field name -especially not when sibling fields are named for other numbers.

Related

How can I treat with NULL as minimum value?

I have a table like this:
// notifications
+----+-----------+-------+---------+---------+------+
| id | score | type | post_id | user_id | seen |
+----+-----------+-------+---------+---------+------+
| 1 | 15 | 1 | 2342 | 342 | 1 |
| 2 | 5 | 1 | 2342 | 342 | 1 |
| 3 | NULL | 2 | 5342 | 342 | 1 |
| 4 | -10 | 1 | 2342 | 342 | NULL |
| 5 | 5 | 1 | 2342 | 342 | NULL |
| 6 | NULL | 2 | 8342 | 342 | NULL |
| 7 | -2 | 1 | 2342 | 342 | NULL |
+----+-----------+-------+---------+---------+------+
-- type: 1 means "it is a vote", 2 means "it is a comment (without score)"
Here is my query:
SELECT SUM(score), type, post_id, seen
FROM notifications
WHERE user_id = 342
GROUP BY type, post_id
ORDER BY (seen IS NULL) desc
As you see, there is SUM() function, Also both type and post_id columns are in the GROUP BY statement. Well now I'm talking about seen column. I don't want to put it into GROUP BY statement. So I have to use either MAX() or MIN() for it. Right?
Actually I need to select NULL as seen column (in query above) if there is even one row which has seen = NULL. My current query selects 1 as seen's value, even when I use MIN(seen). So why 1 is minimum when there is NULL?
Also I want to order rows so that all SEEN = NULL be in the top of list. How can I do that?
Expected result:
// notifications
+-----------+-------+---------+------+
| score | type | post_id | seen |
+-----------+-------+---------+------+
| 13 | 1 | 2342 | NULL |
| NULL | 2 | 8342 | NULL |
| NULL | 2 | 5342 | 1 |
+-----------+-------+---------+------+
You could do this
case when sum(seen is null) > 0
then null
else min(seen)
end
You could use the following query:
SELECT SUM(score), type, post_id, min(IFNULL(seen, 0)) as seen
FROM notifications
WHERE user_id = 342
GROUP BY type, post_id
ORDER BY seen desc

Counting multiple value ocurrences on multiple columns in a single query

I have a search section for looking up products which has a navigation bar for filtering purposes that shows the total results of each product feature. For example:
TOTAL RESULTS 60
New (32)
Used (28)
Particular (10)
Company (50)
In mysql I have the following queries (one per feature)
Type
SELECT a.id_type, whois.name as whoisName, COUNT(a.id_type) as countWhois
FROM (published a
INNER JOIN types whois ON whois.id = a.id_type)
GROUP BY id_type
+---------+------------+------------+
| id_type | whoisName | countWhois |
+---------+------------+------------+
| 0 | Company | 50 |
| 1 | Particular | 10 |
+---------+------------+------------+
Condition
SELECT a.id_condition, cond.name as condName, COUNT(a.id_condition) as countCondition
FROM (published a
INNER JOIN conditions cond ON cond.id = a.id_condition)
GROUP BY id_condition
+--------------+---------------+----------------+
| id_condition | conditionName | countCondition |
+--------------+---------------+----------------+
| 0 | New | 32 |
| 1 | Used | 28 |
+--------------+---------------+----------------+
I want to summarize the two queries in a single one but canĀ“t figure out how. I was thinking something like this:
+---------+------------+------------+--------------+---------------+----------------+
| id_type | whoisName | countWhois | id_condition | conditionName | countCondition |
+---------+------------+------------+--------------+---------------+----------------+
| 0 | Company | 50 | NULL | NULL | NULL |
| 1 | Particular | 10 | NULL | NULL | NULL |
| NULL | NULL | NULL | 0 | New | 32 |
| NULL | NULL | NULL | 1 | Used | 28 |
+---------+------------+------------+--------------+---------------+----------------+
Is this possible?
Thanks and sorry if my English is bad, it's not my native language.

Reorder MySQL rows: move row "up"

I have a MySQL table where rows are inserted in a given order by the user. If the user then forgets to add a row and inserts it later it will come after the others that are already inserted, also when performing simple query. The order of insertion is the order I want the elements to be retreived. The table in question is a simple table resolving a many-to-many relationship with two IDs (recipe_id and ingredient_id).
mysql> select * from ingredient_in_recipe where recipe_id = 7;
+-----------+---------------+------+----------+------------+
| recipe_id | ingredient_id | unit | quantity | group_name |
+-----------+---------------+------+----------+------------+
| 7 | 71 | g | 300.00 | NULL |
| 7 | 34 | stk | 3.00 | NULL |
| 7 | 72 | stk | 1.00 | NULL |
| 7 | 73 | stk | 0.50 | NULL |
| 7 | 45 | stk | 6.00 | NULL |
| 7 | 74 | stk | 0.50 | NULL |
| 7 | 23 | g | 15.00 | NULL |
| 7 | 78 | ts | 2.00 | NULL |
| 7 | 75 | ts | 3.00 | NULL | <--- This is where I want the last element to be.
| 7 | 76 | ss | 1.00 | NULL |
| 7 | 77 | stk | 1.00 | NULL |
| 7 | 79 | g | 195.00 | NULL |
| 7 | 38 | ss | 5.00 | NULL | <--- This is inserted later. Should be "higher up".
+-----------+---------------+------+----------+------------+
13 rows in set (0.00 sec)
Is there a simple way to achieve this?
As pointed out by #C4ud3x and #Hanno Binder, there is no guarantee that MySQL returns the data in the same order as they were inserted. Normally, this is the case, but it is not a robust way to handle ordering of the data. Thus I solved the problem by adding a column order_id to be able to use the ORDER BY clause to ensure that the order will be maintained properly.
I found a good answer to a related question over at dba.stackexchange.com. The main point from the accepted answer there is:
If, on the other hand, you intend to rely on this order for anything, you must specify your desired order using ORDER BY. To do anything else is to set yourself up for unwelcome surprises.

SQL reduce number of columns in inner query

I have a query:
select
count(*), paymentOptionId
from
payments
where
id in (select min(reportDate), id
from payments
where userId in (select distinct userId
from payments
where paymentOptionId in (46,47,48,49,50,51,52,53,54,55,56))
group by userId)
group by
paymentOptionId;
The problem place is "select min(reportDate), id", this query must return 1 column result, but I can't realize how to do it while I need to group min.
The data set looks like
+----+--------+--------+-----------+---------------------+--------+----------+-----------------+
| id | userId | amount | userLevel | reportDate | buffId | bankQuot | paymentOptionId |
+----+--------+--------+-----------+---------------------+--------+----------+-----------------+
| 9 | 12012 | 5 | 5 | 2014-02-10 23:07:57 | NULL | NULL | 2 |
| 10 | 12191 | 5 | 6 | 2014-02-10 23:52:12 | NULL | NULL | 2 |
| 11 | 12295 | 5 | 6 | 2014-02-11 00:12:04 | NULL | NULL | 2 |
| 12 | 12295 | 5 | 6 | 2014-02-11 00:12:42 | NULL | NULL | 2 |
| 13 | 12256 | 5 | 6 | 2014-02-11 00:26:25 | NULL | NULL | 2 |
| 14 | 12256 | 5 | 6 | 2014-02-11 00:26:35 | NULL | NULL | 2 |
| 16 | 12510 | 5 | 5 | 2014-02-11 00:42:58 | NULL | NULL | 2 |
| 17 | 12510 | 5 | 5 | 2014-02-11 00:43:08 | NULL | NULL | 2 |
| 18 | 12510 | 18 | 5 | 2014-02-11 00:45:16 | NULL | NULL | 3 |
| 19 | 12510 | 5 | 6 | 2014-02-11 01:00:10 | NULL | NULL | 2 |
+----+--------+--------+-----------+---------------------+--------+----------+-----------------+
select count(*), paymentOptionId
from
(select userId, min(reportdate), paymentOptionId
from payments as t1
group by userId, paymentOptionId) as t2
group by paymentOptionId
Fiddle
It first gets the minimum report date (so the first entry) for every user, for every type (so there are two records for a user who has 2 types) and then counts them grouping by type (aka paymentOptionId).
By the way, you can of course cut the attributes chosen in select in from clause, they are only there so you can copy-paste it and see the results it is giving step by step.
You seem to want to report on various payment options and their counts for the earliest ReportDate for each user.
If so, here is an alternative approach
select p.paymentOptionId, count(*)
from payments p
where paymentOptionId in (46,47,48,49,50,51,52,53,54,55,56) and
not exists (select 1
from payments p2
where p2.userId = p.userId and
p2.ReportDate < p.ReportDate
)
group by paymentOptionId;
This isn't exactly the same as your query, because this will only report on the list of payment types, whereas you might want the first payment type for anyone who has ever had one of these types. I'm not sure which you want, though.

Distinguish between NULL's when using "group by ... with rollup"

When I run a query using group by ... with rollup:
select a, b, sum(c)
from <table>
group by a, b with rollup;
I get duplicate rows in (what I consider to be) the PK of the query (that is, the group-by columns):
+------+------+--------+
| a | b | sum(c) |
+------+------+--------+
| NULL | NULL | 13 |
| NULL | 1 | 4 |
| NULL | 3 | 8 |
| NULL | 4 | 9 |
| NULL | NULL | 34 |
| 1 | 3 | 17 |
| 1 | 4 | NULL |
| 1 | 17 | 2 |
| 1 | NULL | 19 |
| 2 | NULL | 6 |
| 2 | 1 | 17 |
| 2 | 3 | 17 |
| 2 | NULL | 40 |
| 4 | 17 | 2 |
| 4 | NULL | 2 |
| 5 | NULL | 11 |
| 5 | 6 | 7 |
| 5 | NULL | 18 |
| 13 | 4 | 2 |
| 13 | NULL | 2 |
| 14 | 41 | 3 |
| 14 | NULL | 3 |
| 18 | 1 | 2 |
| 18 | NULL | 2 |
| 41 | 2 | 17 |
| 41 | NULL | 17 |
... more rows follow ...
How do I distinguish (NULL, NULL, 13) from (NULL, NULL, 34)? That is, how do I distinguish between the row that has nulls because of the underlying data, and the row that has nulls because it was added by rollup? (Note that there are more examples -- (2, NULL, 6) and (2, NULL, 40))
Good question. One option I can think of is to do this:
select COALESCE(a, -1) AS a, COALESCE(b, -1) AS b, sum(c)
from <table>
group by COALESCE(a, -1), COALESCE(b, -1) with rollup;
Answer from Cade Roux does not work for me (MySQL v5.1) and seems to be inconsistent from version to version. A method proposed on MySQL documentation comments is the only reliable method I've seen:
http://dev.mysql.com/doc/refman/5.6/en/group-by-modifiers.html
Posted by Peter Kioko on June 27 2012 2:04pm
If you are grouping a column whose data contains NULLs then a NULL in the results is ambiguous as to whether it designates an actual data value or a rolled-up row.
In order to definitively know if a row is a rolled-up row or not you can use this trick:
SET #i = 0;
SELECT #i := #i + 1 AS row_num, year, country, product, SUM(profit) FROM sales GROUP BY year, country, product WITH ROLLUP;
In the result-set, any row whose row_num is the same value as the previous row's row_num is a rolled-up row and vice-versa.