MySQL left outer join trouble - mysql

Here is a query that groups transactions by pricepoint on an hourly basis:
SELECT hour(Stamp) AS hour, PointID AS pricepoint, count(1) AS counter
FROM Transactions
GROUP BY 1,2;
Sample output:
+------+------------+---------+
| hour | pricepoint | counter |
+------+------------+---------+
| 0 | 19 | 5 |
| 0 | 20 | 14 |
| 1 | 19 | 3 |
| 1 | 20 | 12 |
| 2 | 19 | 2 |
| 2 | 20 | 8 |
| 3 | 19 | 2 |
| 3 | 20 | 4 |
| 4 | 19 | 1 |
| 4 | 20 | 1 |
| 5 | 19 | 4 |
| 5 | 20 | 1 |
| 6 | 20 | 2 |
| 8 | 19 | 1 |
| 8 | 20 | 4 |
| 9 | 19 | 2 |
| 9 | 20 | 5 |
| 10 | 19 | 6 |
| 10 | 20 | 1 |
| 11 | 19 | 10 |
| 11 | 20 | 2 |
| 12 | 19 | 10 |
| 12 | 20 | 3 |
| 13 | 19 | 10 |
| 13 | 20 | 10 |
| 14 | 19 | 8 |
| 14 | 20 | 3 |
| 15 | 19 | 6 |
| 15 | 20 | 8 |
| 16 | 19 | 11 |
| 16 | 20 | 10 |
| 17 | 19 | 7 |
| 17 | 20 | 17 |
| 18 | 19 | 7 |
| 18 | 20 | 9 |
| 19 | 19 | 10 |
| 19 | 20 | 12 |
| 20 | 19 | 17 |
| 20 | 20 | 11 |
| 21 | 19 | 12 |
| 21 | 20 | 29 |
| 22 | 19 | 6 |
| 22 | 20 | 21 |
| 23 | 19 | 9 |
| 23 | 20 | 23 |
+------+------------+---------+
As you can see, some hours have no transactions (e.g 7am), and some hours only have transactions for a single pricepoint (e.g. 6am, only pricepoint 20 but no transactions for pricepoint 19).
I would like to display the results set with "0" when there are no transactions, rather than just not being there as is the case now.
Trying to work with a LEFT OUTER JOIN there. The inHour table contains values 0..23
SELECT H.hour, PointID AS Pricepoint, COALESCE(T.counter, 0) AS Count
FROM inHour H
LEFT OUTER JOIN
(
SELECT hour(Stamp) AS Hour, PointID, count(1) AS counter
FROM Transactions
GROUP BY 1,2
) T
ON T.Hour = H.hour;
This produces the following output (truncated for brevity):
| 5 | 19 | 4 |
| 5 | 20 | 1 |
| 6 | 20 | 2 |
| 7 | NULL | 0 |
| 8 | 19 | 1 |
| 8 | 20 | 4 |
What I would like in fact would be:
| 5 | 19 | 4 |
| 5 | 20 | 1 |
| 6 | 19 | 0 |
| 6 | 20 | 2 |
| 7 | 19 | 0 |
| 7 | 20 | 0 |
| 8 | 19 | 1 |
| 8 | 20 | 4 |
In my desired output, the value "0" is put next to pricepoints that had no transactions during a given hour.
Your suggestions would be welcome! Thanks.

SELECT h.Hour, p.Pricepoint, COUNT(t.*) AS Count
FROM inHour h,
(SELECT DISTINCT PointId AS Pricepoint FROM Transactions) p
LEFT OUTER JOIN Transactions t
ON h.Hour = hour(t.Stamp) AND p.Pricepoint = t.PointID
GROUP BY h.Hour, p.Pricepoint
ORDER BY h.Hour, p.Pricepoint
I don't have time at the moment to try this, so let me know if it doesn't work and I'll try to adjust.

Someone probably has a better solution than this, but I would use a UNION to simplify things:
SELECT hour(Stamp) AS hour, PointID AS pricepoint, count(1) AS counter
FROM Transactions
GROUP BY 1,2
UNION
SELECT hour,0 AS pricepoint,0 AS counter FROM inHour WHERE hour NOT IN (SELECT hour(Stamp) FROM Transactions)

Related

How to compare a new value to last value in the table

I am trying to create a trigger in MySQL database. I have a table (myData) with 3 columns. Date, Values, and Status. What I am trying to achieve is, when a new value comes, if it is higher than the last value, It should insert 1 in the Status column. If it is less than the last value, It should insert 0 in the status column. I couldn't find a logic to do it. Any suggestions, please?
BEGIN
IF new.Values > // what should be here?
THEN
INSERT INTO //
END
Instead consider the following:
SELECT * FROM my_table;
+----+-------+
| id | value |
+----+-------+
| 1 | 2 |
| 2 | 12 |
| 3 | 13 |
| 4 | 9 |
| 5 | 7 |
| 6 | 6 |
| 7 | 8 |
| 8 | 3 |
| 9 | 10 |
| 10 | 1 |
| 11 | 18 |
| 12 | 4 |
| 13 | 6 |
| 14 | 0 |
| 15 | 2 |
| 16 | 8 |
| 17 | 14 |
| 18 | 7 |
| 19 | 15 |
| 20 | 11 |
| 21 | 12 |
| 22 | 7 |
| 23 | 20 |
| 24 | 17 |
| 25 | 8 |
| 26 | 6 |
| 27 | 6 |
| 28 | 12 |
| 29 | 3 |
| 30 | 18 |
| 31 | 1 |
| 32 | 12 |
+----+-------+
SELECT a.*
, b.value >= a.value n
FROM my_table a
LEFT
JOIN
( SELECT x.*
, MIN(y.id) next
FROM my_table x
LEFT
JOIN my_table y
ON y.id > x.id
GROUP
BY x.id
) b
ON b.next = a.id;
+----+-------+------+
| id | value | n |
+----+-------+------+
| 1 | 2 | NULL |
| 2 | 12 | 0 |
| 3 | 13 | 0 |
| 4 | 9 | 1 |
| 5 | 7 | 1 |
| 6 | 6 | 1 |
| 7 | 8 | 0 |
| 8 | 3 | 1 |
| 9 | 10 | 0 |
| 10 | 1 | 1 |
| 11 | 18 | 0 |
| 12 | 4 | 1 |
| 13 | 6 | 0 |
| 14 | 0 | 1 |
| 15 | 2 | 0 |
| 16 | 8 | 0 |
| 17 | 14 | 0 |
| 18 | 7 | 1 |
| 19 | 15 | 0 |
| 20 | 11 | 1 |
| 21 | 12 | 0 |
| 22 | 7 | 1 |
| 23 | 20 | 0 |
| 24 | 17 | 1 |
| 25 | 8 | 1 |
| 26 | 6 | 1 |
| 27 | 6 | 1 |
| 28 | 12 | 0 |
| 29 | 3 | 1 |
| 30 | 18 | 0 |
| 31 | 1 | 1 |
| 32 | 12 | 0 |
+----+-------+------+
If all you want is the rows where n=1, then the query is actually even simpler...
SELECT a.*
FROM my_table a
JOIN
( SELECT x.*
, MIN(y.id) next
FROM my_table x
LEFT
JOIN my_table y
ON y.id > x.id
GROUP
BY x.id
) b
ON b.next = a.id
AND b.value >= a.value;
+----+-------+
| id | value |
+----+-------+
| 4 | 9 |
| 5 | 7 |
| 6 | 6 |
| 8 | 3 |
| 10 | 1 |
| 12 | 4 |
| 14 | 0 |
| 18 | 7 |
| 20 | 11 |
| 22 | 7 |
| 24 | 17 |
| 25 | 8 |
| 26 | 6 |
| 27 | 6 |
| 29 | 3 |
| 31 | 1 |
+----+-------+

Group by functioning in sql [duplicate]

This question already has answers here:
MySQL - Selecting a Column not in Group By
(4 answers)
SQL select only rows with max value on a column [duplicate]
(27 answers)
Closed 5 years ago.
I am running below queries on my table.
Table:
+----+-------------+--------------+----------------------------+------------+--------+
| id | Qty_holding | Qty_reserved | created | tokenid_id | uid_id |
+----+-------------+--------------+----------------------------+------------+--------+
| 1 | 10 | 0 | 2018-01-18 10:52:14.957027 | 1 | 1 |
| 2 | 20 | 0 | 2018-01-18 11:20:08.205006 | 8 | 1 |
| 3 | 110 | 0 | 2018-01-18 11:20:21.496318 | 14 | 1 |
| 4 | 10 | 0 | 2018-01-23 14:26:49.124607 | 1 | 2 |
| 5 | 3 | 0 | 2018-01-23 15:00:26.876623 | 11 | 2 |
| 6 | 7 | 0 | 2018-01-23 15:08:41.887240 | 11 | 2 |
| 7 | 11 | 0 | 2018-01-23 15:22:48.424224 | 11 | 2 |
| 8 | 15 | 0 | 2018-01-23 15:24:03.419907 | 11 | 2 |
| 9 | 19 | 0 | 2018-01-23 15:24:26.531141 | 11 | 2 |
| 10 | 23 | 0 | 2018-01-23 15:27:11.549538 | 11 | 2 |
| 11 | 27 | 0 | 2018-01-23 15:27:24.162944 | 11 | 2 |
| 12 | 7.7909428 | 0.11459088 | 2018-01-23 15:27:24.168643 | 1 | 2 |
| 13 | 3 | 0 | 2018-01-23 15:36:51.412340 | 14 | 2 |
| 14 | 7.5585988 | 0.11459088 | 2018-01-23 15:36:51.417177 | 1 | 2 |
| 15 | 6 | 0 | 2018-01-24 08:43:46.635069 | 14 | 2 |
| 16 | 7.3262548 | 0.11459088 | 2018-01-24 08:43:46.639984 | 1 | 2 |
| 17 | 9 | 0 | 2018-01-24 10:09:08.207816 | 14 | 2 |
| 18 | 7.0939108 | 0.11459088 | 2018-01-24 10:09:08.212842 | 1 | 2 |
| 19 | 6 | 3 | 2018-01-24 13:43:08.929586 | 14 | 2 |
| 20 | 3 | 6 | 2018-01-24 14:49:56.960112 | 14 | 2 |
| 21 | 0 | 9 | 2018-01-24 14:50:33.423671 | 14 | 2 |
| 22 | 30 | 9 | 2018-01-24 14:51:14.865453 | 14 | 2 |
| 23 | 4.7704708 | 0.11459088 | 2018-01-24 14:51:14.870256 | 1 | 2 |
| 24 | 27 | 12 | 2018-01-24 14:56:56.914009 | 14 | 2 |
| 25 | 24 | 15 | 2018-01-24 14:57:56.475939 | 14 | 2 |
| 26 | 21 | 15 | 2018-01-24 14:58:06.750903 | 14 | 2 |
| 27 | 18 | 15 | 2018-01-24 15:02:43.203878 | 14 | 2 |
| 28 | 4.7705074 | 0.11459088 | 2018-01-24 15:02:43.224901 | 1 | 2 |
| 29 | 24 | 0 | 2018-01-24 15:03:40.421943 | 11 | 2 |
| 30 | 4.9535074 | 0.11459088 | 2018-01-24 15:03:40.441552 | 1 | 2 |
| 31 | 1 | 0 | 2018-01-26 10:35:33.173801 | 18 | 2 |
| 32 | 10 | 15 | 2018-01-26 12:46:03.780807 | 14 | 2 |
+----+-------------+--------------+----------------------------+------------+--------+
Query 1:
select uid_id
, tokenid_id
, max(created) as max_created
from accounts_userholding
group
by uid_id
, tokenid_id
+--------+------------+----------------------------+
| uid_id | tokenid_id | max_created |
+--------+------------+----------------------------+
| 1 | 1 | 2018-01-18 10:52:14.957027 |
| 1 | 8 | 2018-01-18 11:20:08.205006 |
| 1 | 14 | 2018-01-18 11:20:21.496318 |
| 2 | 1 | 2018-01-24 15:03:40.441552 |
| 2 | 11 | 2018-01-24 15:03:40.421943 |
| 2 | 14 | 2018-01-26 12:46:03.780807 |
| 2 | 18 | 2018-01-26 10:35:33.173801 |
+--------+------------+----------------------------+
Query 2:
select uid_id
, Qty_holding
, Qty_reserved tokenid_id
, max(created) as max_created
from accounts_userholding
group
by uid_id
, tokenid_id
+--------+-------------+--------------+------------+----------------------------+
| uid_id | Qty_holding | Qty_reserved | tokenid_id | max_created |
+--------+-------------+--------------+------------+----------------------------+
| 1 | 10 | 0 | 1 | 2018-01-18 10:52:14.957027 |
| 1 | 20 | 0 | 8 | 2018-01-18 11:20:08.205006 |
| 1 | 110 | 0 | 14 | 2018-01-18 11:20:21.496318 |
| 2 | 10 | 0 | 1 | 2018-01-24 15:03:40.441552 |
| 2 | 3 | 0 | 11 | 2018-01-24 15:03:40.421943 |
| 2 | 3 | 0 | 14 | 2018-01-26 12:46:03.780807 |
| 2 | 1 | 0 | 18 | 2018-01-26 10:35:33.173801 |
+--------+-------------+--------------+------------+----------------------------+
The Qty_holding value in above is not corresponding to latest date. For instance for tokenid_id 14 and uid_id as 2 latest record is
| 32 | 10 | 15 | 2018-01-26 12:46:03.780807 | 14 | 2 |
But above query is giving qty_holding as 3.
Any insights in functioning of mysql will be helpful . Thanks!
As a rule of thumb: When you mix normal columns with aggregate functions in SELECT, you need to use GROUP BY. Do not use GROUP BY when you do not have normal columns and aggregate functions in SELECT.
The thing to put into the GROUP BY, is all from SELECT but the aggregate functions (and possible constants).
As an example if you have a query:
select a, substring(b,3), 'x', max(y)
from yourtable
You need to use GROUP BY. You leave out 'x' as it is a constant and you leave out the aggregate function. The rest goes to the GROUP BY.
select a, substring(b,3), 'x', max(y)
from yourtable
group by a, substring(b,3)
Previous MySQL versions allowed quite liberal use of GROUP BY resulting quite often just bad/incorrect code.

MySQL generate all possible combinations from numbers with a certain length

how to generate all possible combinations from numbers in a MySql table with a certain length? For example 8 numbers.
Only with MySql not together with php.
My table "numbers" contains:
0
1
2
3
4
5
6
7
8
9
I want now to let create all possible combinations in "numbers1".
Like:
00000001
00000002
00000003
00000004
It sounds like you want a cartesian product but I don't get why you want a single column in your output but anyway
select distinct case when u1.id >= u.id then u.id else u1.id end umin,
case when u1.id < u.id then u.id else u1.id end umax
from users u cross join users u1
where u1.id <> u.id
order by umin,umax
The cross join creates the cartesian product the where clause drops those where the combination/permuatation results in the same value eg 1,1 2,2 etc and the distinct dedupes
So given this
MariaDB [sandbox]> select id from users;
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
| 6 |
| 7 |
| 8 |
| 10 |
| 12 |
| 14 |
| 15 |
| 16 |
| 17 |
| 18 |
| 19 |
| 20 |
+----+
15 rows in set (0.00 sec)
The query results in
+------+------+
| umin | umax |
+------+------+
| 1 | 2 |
| 1 | 3 |
| 1 | 6 |
| 1 | 7 |
| 1 | 8 |
| 1 | 10 |
| 1 | 12 |
| 1 | 14 |
| 1 | 15 |
| 1 | 16 |
| 1 | 17 |
| 1 | 18 |
| 1 | 19 |
| 1 | 20 |
| 2 | 3 |
| 2 | 6 |
| 2 | 7 |
| 2 | 8 |
| 2 | 10 |
| 2 | 12 |
| 2 | 14 |
| 2 | 15 |
| 2 | 16 |
| 2 | 17 |
| 2 | 18 |
| 2 | 19 |
| 2 | 20 |
| 3 | 6 |
| 3 | 7 |
| 3 | 8 |
| 3 | 10 |
| 3 | 12 |
| 3 | 14 |
| 3 | 15 |
| 3 | 16 |
| 3 | 17 |
| 3 | 18 |
| 3 | 19 |
| 3 | 20 |
| 6 | 7 |
| 6 | 8 |
| 6 | 10 |
| 6 | 12 |
| 6 | 14 |
| 6 | 15 |
| 6 | 16 |
| 6 | 17 |
| 6 | 18 |
| 6 | 19 |
| 6 | 20 |
| 7 | 8 |
| 7 | 10 |
| 7 | 12 |
| 7 | 14 |
| 7 | 15 |
| 7 | 16 |
| 7 | 17 |
| 7 | 18 |
| 7 | 19 |
| 7 | 20 |
| 8 | 10 |
| 8 | 12 |
| 8 | 14 |
| 8 | 15 |
| 8 | 16 |
| 8 | 17 |
| 8 | 18 |
| 8 | 19 |
| 8 | 20 |
| 10 | 12 |
| 10 | 14 |
| 10 | 15 |
| 10 | 16 |
| 10 | 17 |
| 10 | 18 |
| 10 | 19 |
| 10 | 20 |
| 12 | 14 |
| 12 | 15 |
| 12 | 16 |
| 12 | 17 |
| 12 | 18 |
| 12 | 19 |
| 12 | 20 |
| 14 | 15 |
| 14 | 16 |
| 14 | 17 |
| 14 | 18 |
| 14 | 19 |
| 14 | 20 |
| 15 | 16 |
| 15 | 17 |
| 15 | 18 |
| 15 | 19 |
| 15 | 20 |
| 16 | 17 |
| 16 | 18 |
| 16 | 19 |
| 16 | 20 |
| 17 | 18 |
| 17 | 19 |
| 17 | 20 |
| 18 | 19 |
| 18 | 20 |
| 19 | 20 |
+------+------+
105 rows in set (0.00 sec)
if the length is fixed it is quite simple, so you could do something like this
DECLARE #number TABLE (num NVARCHAR(1))
INSERT INTO #number
VALUES ('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9')
SELECT n1.num + n2.num + n3.num + n4.num
FROM #number n1, #number n2, #number n3, #number n4
if the number is variable you could either build the sql-command and execute it using sp_execute #cmd or you can create a table-value-function for it.
Edit: I just overread the "mysql"-part. the solution above is coded in TSQL (using MS SqlServer). I am not completely sure, but the only thing you have to change should be the declaration of the variable.
CREATE TEMPORARY TABLE #number (num NVARCHAR(1))
And if I am not mistaken instead of using sp_executeto execute a command you can use something like
PREPARE cmd FROM 'SELECT ...'
EXECUTE cmd

Date less than MySQL query not working

I've a table
| adsid | user_id | earned_points | redeem_points | dialer_point | app_point | date |
+-------+---------+---------------+---------------+--------------+-----------+---------------------+
| 1 | 1 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 3 | 1 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 2 | 1 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 1 | 2 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 3 | 2 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 2 | 2 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 1 | 3 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 3 | 3 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 2 | 3 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 1 | 4 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 3 | 4 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 2 | 4 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 1 | 5 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 3 | 5 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 2 | 5 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 1 | 6 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 3 | 6 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 2 | 6 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 1 | 7 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 3 | 7 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 2 | 7 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 1 | 8 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 3 | 8 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
| 2 | 8 | 25 | 15 | 23 | 2 | 2015-03-15 19:38:43 |
+-------+---------+---------------+---------------+--------------+-----------+---------------------+
The following less than date query does not works
select * from USER_POINTS_MAPPING where 'date' < '2015-03-17';
But when I do
select * from USER_POINTS_MAPPING where 'date' > '2015-03-17';
It throws back all the rows. What is this happening?
Try without ' characters (single quotation mark) around date. In MySQL either don't use any quotation mark or use this one ` (backtick) around field names.
'date' means date as string. And 'date' is always greater than '2015-03-17' when they are compared as string
While
`date`
means date as a field name
So the correct query is:
select * from USER_POINTS_MAPPING where date < '2015-03-17';
Pro tip: Don't use date as a column name. It's a reserved word. If you must use it surround it with backticks, not quotes.
where `data` < '2015-03-17'

MySQL - How to use GROUP BY / ORDER BY with "nested" dataset?

My (sub)query results in following dataset:
+---------+------------+-----------+
| item_id | version_id | relevance |
+---------+------------+-----------+
| 1 | 1 | 30 |
| 1 | 2 | 30 |
| 2 | 3 | 22 |
| 3 | 4 | 30 |
| 4 | 5 | 18 |
| 3 | 6 | 30 |
| 2 | 7 | 22 |
| 1 | 8 | 30 |
| 5 | 9 | 48 |
| 4 | 10 | 18 |
| 5 | 11 | 48 |
| 3 | 12 | 30 |
| 3 | 13 | 31 |
| 4 | 14 | 19 |
| 2 | 15 | 22 |
| 1 | 16 | 30 |
| 5 | 17 | 49 |
| 2 | 18 | 22 |
+---------+------------+-----------+
18 rows in set (0.00 sec)
Items and versions are stored in separate InnoDB-tables.
Both tables have auto-incrementing primary keys.
Versions have a foreign key to items (item_id).
My question: How do I get a subset based on relevance?
I would like to fetch the following subset containing the most relevant versions:
+---------+------------+-----------+
| item_id | version_id | relevance |
+---------+------------+-----------+
| 1 | 16 | 30 |
| 2 | 18 | 22 |
| 3 | 13 | 31 |
| 4 | 14 | 19 |
| 5 | 17 | 49 |
+---------+------------+-----------+
It would be even more ideal to fetch the MAX(version_id) in case of equal relevance.
I tried grouping, joining, ordering, etcetera in many ways but I'm not able to get the desired result.
Some of the things I tried is:
SELECT item_id, version_id, relevance
FROM (subquery) a
GROUP BY item_id
ORDER BY relevance DESC, version_id DESC
But of course the ordering happens after the fact, so that both relevance and MAX(version_id) information is lost.
Please advice.
This is how you can do this:
SELECT t1.item_id, max(t1.version_id), t1.relevance FROM t t1
LEFT JOIN t t2 ON t1.item_id = t2.item_id AND t1.relevance < t2.relevance
WHERE t2.relevance IS NULL
GROUP BY t1.item_id
ORDER BY t1.item_id, t1.version_id
Output:
| ITEM_ID | VERSION_ID | RELEVANCE |
|---------|------------|-----------|
| 1 | 16 | 30 |
| 2 | 18 | 22 |
| 3 | 13 | 31 |
| 4 | 14 | 19 |
| 5 | 17 | 49 |
Fiddle here.