MySQL union is not deterministic - mysql

Here is two equals query at first sight:
SELECT obj_id
FROM obj t JOIN joined_a a ON t.`id` = a.`obj_id`
UNION
SELECT obj_id
FROM obj t JOIN joined_b b ON t.`id` = b.`obj_id`;
and
SELECT obj_id
FROM obj t JOIN joined_b b ON t.`id` = b.`obj_id`
UNION
SELECT obj_id
FROM obj t JOIN joined_a a ON t.`id` = a.`obj_id`;
They are different only with select's sequence.
result rows quantity from these two queries are different!
How does it possible?
If I add DISTINCT to each SELECT ... rows quantity will have NEW value, the biggest value!
If I add brackets to each SELECT like
(select ...)
UNION
(select ...)
rows quantity will have the DISTINCT's value.

These two queries will return the same rows, but not necessarily in the same order.
UNION dedupes the results for you, so the DISTINCTs do nothing and the size of the result set is the same regardless of the SELECT order.
The only explanation that fits your test is that some more data was inserted in between the running of the two.

You are right!
I got into an insidious trap with SQLYog ultimate GUI interface v11.11 BUG-feacher!!!
GUI adds to each query LIMIT 0, 1000; suffix
:(
check queries at command line and it's work fine.

Related

How to get information from two tables

I want to extract some information from two different table in one database,
[(first table): id-nbrNight-........]
[(second table): id-........]
I want to extract the nbrNight from the first table & the id from the second table:
so in my case I write this, but I don't know how to rassemble this two line in one line:
SELECT sum(nbrNight) as night FROM firsttab
SELECT count(`id`) as id FROM secondtab
I wirte this to rassemble this two line:
SELECT sum(nbrNight) as night,count(`id`) FROM firsttab,secondtab
But it doesn't work!
You can use UNION to combine the result from two query like
SELECT sum(nbrNight) as night FROM firsttab
UNION
SELECT count(`id`) as id FROM secondtab
(OR) do a JOIN with both tables using a common column between them (if any present) like below (assuming id is the common column between them)
SELECT sum(t1.nbrNight) as nightsum, count(t2.`id`) as idcount
FROM firsttab t1 JOIN secondtab t2 ON t1.id = t2.id
One option is to use the queries as inline views; reference those queries as a rowsource (like a table) in another query.
Since each query returns a single row, you can safely perform a JOIN operation, without need for any join predicate (aka CROSS JOIN).
For example:
SELECT f.night
, s.id
FROM ( SELECT SUM(nbrNight) AS night FROM firsttab ) f
CROSS
JOIN ( SELECT COUNT(id) AS id FROM secondtab ) s
Another option (since both queries are guaranteed to return exactly one row, if they don't return an error) is to include the query in the SELECT list of another query. It's not necessary to include a column alias in the subquery, but we can assign an alias.
For example:
SELECT ( SELECT SUM(nbrNight) FROM firsttab ) AS night
, ( SELECT COUNT(id) FROM secondtab ) AS id
If either of the queries were returning more than one column, then the approach in the first example will still work. The inline view queries can return multiple expressions, and we can reference those expressions in the outer query. With the pattern in the second example, that imposes a restriction that the subqueries must return only one expression (one column).
As an example to demonstrate an inline view returning more than one column, the inline view f returns three expressions:
SELECT f.night
, f.cnt
, f.min_nbr
, s.id
FROM ( SELECT SUM(nbrNight) AS night
, COUNT(nbrNight) AS cnt
, MIN(nbrNight) AS min_nbr
FROM firsttab
) f
CROSS
JOIN ( SELECT COUNT(id) AS id FROM secondtab ) s

I want row number in view Or variable in View

I know it is not possible directly.
But I want to achieve this by any indirect method if possible.
Actually I wanted to add below query to view which throws error , Sub query not allowed in view.
select T1.Code,
T1.month,
T1.value,
IfNull(T2.Value,0)+IfNull(T3.value,0) as value_begin
from (select *,#rownum := #rownum + 1 as rownum
from Table1
Join (SELECT #rownum := 0) r) T1
left join (select *,#rownum1 := #rownum1 + 1 as rownum
from Table1
Join (SELECT #rownum1 := 0) r) T2
on T1.code = T2.code
and T1.rownum = T2.rownum + 1
left join (select *,#rownum2 := #rownum2 + 1 as rownum
from Table1
Join (SELECT #rownum2 := 0) r) T3
on T1.code = T3.code
and T1.rownum = T3.rownum + 2
Order by T1.Code,T1.rownum
So, I thought I will make Sub query as separate view but that again throws error that variables not allowed in view. Please Help to overcome this situation.
Thanx in advance
You could try the method of triangle join + count for assigning row numbers. It will likely not perform well on large datasets, but instead you should be able to implement everything with a couple of views (if you think there's no other way to do what you want to do than with a view). The idea is as follows:
The dataset is joined to itself on the condition of master.key >= secondary.key, where master is the instance where detail data will actually be pulled from, and secondary is the other instance of the same table used to provide the row numbers.
Based on that condition, the first* master row would be joined with one secondary row, the second one with two, the third one with three and so on.
At this point, you can group the result set by the master key column(s) as well as the columns that you need in the output (although in MySQL it would be enough to group by the master key only). Count the rows in every group will give you corresponding row numbers.
So, if there was a table like this:
CREATE TABLE SomeTable (
ID int,
Value int
);
the query to assign row numbers to the table could look like this:
SELECT m.ID, m.Value, COUNT(*) AS rownum
FROM SomeTable AS m
INNER JOIN SomeTable AS s ON m.ID >= s.ID
GROUP BY m.ID, m.Value
;
Since you appear to want to self-join the ranked rowset (and twice too), that would require using the above query as a derived table, and since you also want the entire thing to be a view (which doesn't allow subqueries in the FROM clause), you would probably need to define the ranking query as a separate view:
CREATE RankingView AS
SELECT m.ID, m.Value, COUNT(*) AS rownum
FROM SomeTable AS m
INNER JOIN SomeTable AS s ON m.ID >= s.ID
GROUP BY m.ID, m.Value
;
and subsequently refer to that view in the main query:
CREATE SomeOtherView AS
SELECT ...
FROM RankingView AS t1
LEFT JOIN RankingView AS t2 ON ...
...
This SQL Fiddle demo shows the method and its usage.
One note with regard to your particular situation. Your table probably needs row numbers to be assigned in partitions, i.e. every distinct Code row group needs its own row number set. That means that your ranking view should specify the joining condition as something like this:
ON m.Code = s.Code AND m.Month >= s.Month
Please note that months in this case are assumed to be unique per Code. If that is not the case, you may first need to create a view that groups the original dataset by Code, Month and rank that view instead of the original dataset.
* According to the order of key.

Quick SELECT WHERE IN like with flag

I have a list of ids and need to check whether user with id is in DB or not in one SELECT. Like SELECT WHERE IN (). But SELECT WHERE IN () doesn't suit my needs, I need in one SELECT distinguish those ids that are in table, and those that are not, not using any loops like multiple SELECTS. Any ideas are welcome!
I'm not sure if this is what you need, but I guess you have table 1 which contains a lot of IDs, and you would like to see which ones occur in table 2 and which ones don't?
select T1.ID, count(*) 'Times of occurrences in T2'
from table 1 T1
left outer join table 2 T2
ON T1.ID = T2.ID
group by T1.ID
You should provide more details. Would it be a single query so a list could be hardcoded into query or you want to find general solution for any list of ids provided? How long is your list?
For single query and not very long list you can use union. On example:
SELECT some_value, EXISTS( SELECT 1 FROM tableName WHERE user_id = some_value )
UNION ALL
SELECT other_value, EXISTS( SELECT 1 FROM tableName WHERE user_id = other_value )
UNION ALL
SELECT other_value2, EXISTS( SELECT 1 FROM tableName WHERE user_id = other_value2 )
UNION ALL
.....
If your list of ids can vary and/or consists of thousands of records it is impossible. In list you have columnar layout and you want to change it to row-level results. In MsSQL there are PIVOT, UNPIVOT clauses which can do that. In MySQL such transformation without explicit unions are impossible.

Check membership of elements of one column in another, mySQL

How would I go about counting values that appear in column 1, but not column 2. They are from the same table, without using subqueries or anything fancy. They may or may not share other common column values (like col 3 = col 4) but this doesnt matter.
I have it almost working with subqueries, but cannot figure how to do it without. The only problem (I think) is it will count something twice if the primary key (composed of col1,col3,col4) are different but col1 is the same.
SELECT DISTINCT COUNT(*)
FROM mytable t1
WHERE NOT EXISTS (
SELECT DISTINCT *
FROM mytable
WHERE t1.column1 = mytable.column2
);
But like I said, I'm trying to figure this without subqueries anyways
How about:
SELECT COUNT(*)
FROM mytable mt1
LEFT JOIN mytable mt2 ON mt1.column1 = mt2.column2
WHERE mt2.column IS NULL
Please see this:
SELECT
SUM(IF(column1 = column2, 0, 1)) as c
FROM
mytable

How to join results from two tables in one output file?

I have two tables in MySQL DB; table1, table2.
Both of them have a single column (float) values. It's actually a dump from our research project which produces a single value result.
And many of these values get repeated and sorting and filtering them in Python would be cumbersome, so I though perhaps dumping them in a table in DB would be quicker.
So the end result from the SQL query is the following grouped by the value:
value table1_count table2_count
1.0 0 1
1.1 1 3
2.1 4 5
The query I am coming up with is the following:
select everything.value, everything.count1, everything.count2
from
((
select X as value, count(*) from table1
) union all (
select X as value, count (*) from table2
)) everything
group by everything.value
into outfile "/count";
Any suggestions?
Thanks,
You can't do counts by group in the inner queries, since you're defining the groups in the outer query. This should be simpler:
select everything.value, count(*)
from
(
select X as value from table1
union all
select X from table2
) everything
group by value
into outfile "/count";
Also here's some trivia: when you use UNION, you need to define column aliases only in the first query unioned.
Re your comment. Here's one solution:
select everything.value, sum(t = 'success') as s, sum(t = 'failure') as f
from
(
select X as value, 'success' as t from table1
union all
select X, 'failure' from table2
) everything
group by value
into outfile "/count";
This uses a trick in MySQL that boolean expressions return 0 for false or 1 for true. So when you sum up a bunch of expressions, you get a count of the rows where the expression is true. (Don't rely on this trick in other brands of SQL database.)