i'm having trouble of finding a way to perform simple addition on a table of maria db, SQL.
i'm having a table called Traffic:
| start_time | end_time | col1 | col2 |
| 1485075600.000000 | 1485075900.000000 | 10 | 20 |
| 1485075900.000000 | 1485076200.000000 | 20 | 30 |
| 1485076200.000000 | 1485076500.000000 | 40 | 50 |
| 1485076500.000000 | 1485076800.000000 | 50 | 60 |
How can i sum every N columns (over col1, and col2) ?
i mean, to merge rows and sum the values of col1, and col2.
assuming the given table, And N = 2,
the result will be:
| start_time | end_time | col1 | col2|
| 1485075600.000000 | 1485076200.000000 | 30 | 50 |
| 1485076200.000000 | 1485076800.000000 | 90 | 110 |
if the table size isn't a multiple of of N, take all you can.
Any one have any idea? i don't have id's to group by on.
You would do this by enumerating the rows. To be correct, you need a column to specify the ordering -- SQL tables represent unordered sets, so they need a column for the ordering.
Let me assume it is start_time. The rest is just aggregation and arithmetic:
select min(start_time) as start_time, max(end_time) as end_time,
sum(col1) as col1, sum(col2) as col2
from (select t.*, (#rn := #rn + 1) as rn
from traffic t cross join
(select #rn := 0) params
order by start_time
) t
group by floor( (rn - 1) / #N);
The #N value is the size of the groups.
Related
I have the following column names:
customer_email
increment_id
other_id (psuedo name)
created_at
increment_id and other_id will be unique, customer_email will have duplicates. As the results are returned I want to know what number of occurance of the email it is.
For each row, I want to know how many times thecustomer_email value has shown up so far. There will be an order by clause at the end for the created_at field and I plan to also add a where clause of where occurrences < 2
I am querying > 5 million rows but performance isn't too important because I'll be running this as a report on a read-replica database from production. In my use case, I will sacrifice performance for robustness.
| customer_email | incremenet_id | other_id | created_at | occurances <- I want this |
|----------------|---------------|----------|---------------------|---------------------------|
| joe#test.com | 1 | 81 | 2019-11-00 00:00:00 | 1 |
| sue#test.com | 2 | 82 | 2019-11-00 00:01:00 | 1 |
| bill#test.com | 3 | 83 | 2019-11-00 00:02:00 | 1 |
| joe#test.com | 4 | 84 | 2019-11-00 00:03:00 | 2 |
| mike#test.com | 5 | 85 | 2019-11-00 00:04:00 | 1 |
| sue#test.com | 6 | 86 | 2019-11-00 00:05:00 | 2 |
| joe#test.com | 7 | 87 | 2019-11-00 00:06:00 | 3 |
You can use variables in earlier versions of MySQL:
select t.*,
(#rn := if(#ce = customer_email, #rn + 1,
if(#ce := customer_email, 1, 1)
)
) as occurrences
from (select t.*
from t
order by customer_email, created_at
) t cross join
(select #ce := '', #rn := 0) params;
In MyQL 8+, I would recommend row_number():
select t.*,
row_number() over (partition by customer_email order by created_at) as occurrences
from t;
If you are running MySQL 8.0, you can just do a window count:
select
t.*,
count(*) over(partition by customer_email order by created_at) occurences
from mytable t
You don't need an order by clause at the end of the query for this to work (but you need one if you want to order the results).
If you need to filter on the results of the window count, an additional level is needed, since window functions cannot be used in the where clause of a query:
select *
from (
select
t.*,
count(*) over(partition by customer_email order by created_at) occurences
from mytable t
) t
where occurences < 2
I have table my_table which contains groups of categories, each category has initial budget (original_budget):
I am trying to add a new column balance so it contains the balance after reducing expense from the original_budget in each category group. Something like:
my try:
SELECT category, expense, original_budget, (original_budget-expense) AS balance
FROM my_table GROUP BY category order by `trans_date`
MySQL version: innodb_version 5.7.25
10.2.23-MariaDB
If you are using MySQL 8+, then it is fairly straightforward to use SUM here as a window function:
SELECT
trans_date,
category,
expense,
original_budget,
original_budget - SUM(expense) OVER
(PARTITION BY category
ORDER BY trans_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) balance
FROM my_table
ORDER BY
category,
trans_date;
Demo
On earlier versions of MySQL, we can try to compute the rolling balance using a correlated subquery:
SELECT
trans_date,
category,
expense,
original_budget,
original_budget - (SELECT SUM(t2.expense) FROM my_table t2
WHERE t1.category = t2.category AND
t2.trans_date <= t1.trans_date) balance
FROM my_table t1
ORDER BY
category,
trans_date;
Demo
For All MySQL versions:
You can use MySQL User defined Variable to reduce balance amount for a category. For this keep same category records together with sorted dates.
SELECT
category,
expense,
original_budget,
IF(#cat <> category, #budg:= original_budget - expense, #budg:= #budg - expense) AS balance,
#cat:= category -- Set category to current value so we can compare it in next iteration
FROM my_table,
(SELECT #cat:= '' AS c, #budg:= NULL AS b) AS t
ORDER BY category, `trans_date`;
Output:
| category | expense | original_budget | balance | #cat:= category |
| A | 10 | 100 | 90 | A |
| A | 2 | 100 | 88 | A |
| A | 1 | 100 | 87 | A |
| B | 12 | 300 | 288 | B |
| B | 1 | 300 | 287 | B |
| B | 1 | 300 | 286 | B |
| B | 1 | 300 | 285 | B |
I'm trying to create a query that creates a column that adds all the row value of the previous column per row. I've tried SUM and COUNT but this is not giving me the result I want. How should I tackle this problem?
+----+------+-----+
| id |amount|total|
+----+------+-----+
| 1 | 10 | 10 |
| 2 | 20 | 30 |
| 3 | 15 | 45 |
| 4 | 30 | 75 |
+----+------+-----+
It is a Rolling Sum problem. In MySQL 8.0.2 and above, you can solve this using Window functions with Frames. In older versions, we can do the same using User-defined Session variables.
Try:
SELECT
dt.id,
dt.amount,
#tot := #tot + dt.amount AS total
FROM
(
SELECT
id,
amount
FROM your_table_name
ORDER BY id
) AS dt
CROSS JOIN (SELECT #tot := 0) AS user_init
I am struggling with this complex query. I am trying to insert the order position of some products.
For example,
I have currently table 1 with a position of NULL, I want to group each Product ID and assign each size a menu position based on ProductID group and using this FIND_IN_SET:
FIND_IN_SET(size,"UNI,XS,S,M,L,XL,XXL,3XL,4XL,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60") asc;
In other words, I want it to look like Table2.
Table1
ID | ProductID | Size | Menu_position
1 | 100 | S | NULL
2 | 100 | M | NULL
3 | 100 | L | NULL
4 | 101 | 40 | NULL
5 | 101 | 41 | NULL
6 | 101 | 42 | NULL
7 | 102 | XS | NULL
8 | 102 | L | NULL
Table2
ID | ProductID | Size | Menu_position
1 | 100 | S | 1
2 | 100 | M | 2
3 | 100 | L | 3
4 | 101 | 40 | 1
5 | 101 | 41 | 2
6 | 101 | 42 | 3
7 | 102 | XS | 1
8 | 102 | L | 2
What I collected so far:
Number of products Group:select count(distinct ProductID) from Table1
Sort size based on specific order: SELECT * FROM Table1 ORDER BY FIND_IN_SET(size,"UNI,XS,S,M,L,XL,XXL,3XL,4XL,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60") asc;
You can use variables in pre-MySQL 8.0:
SELECT t1.*,
(#rn := if(#p = productid, #rn + 1,
if(#p := productid, 1, 1)
)
) as menu_position
FROM (SELECT t1.*
FROM Table1 t1
ORDER BY ProductId,
FIND_IN_SET(size, 'UNI,XS,S,M,L,XL,XXL,3XL,4XL,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60') asc
) AS alias CROSS JOIN
(SELECT #p := -1, #rn := 0) params;
In MySQL 8+, this is much simpler:
select t1.*,
row_number() over (partition by productid order by FIND_IN_SET(size, 'UNI,XS,S,M,L,XL,XXL,3XL,4XL,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60')) as menu_position
from table1 t1
Create a two-column table containing all the the Size values in one column and the integer order of those sizes in the second column--call that table menu_pos. Join this to your Table on size, to produce a table or view (call this product_pos) containing columns product_id, size, and menu_pos. Then modify the menu_pos values to ensure that they are strictly sequential using a window function, such as:
select
product_id,
size,
rank() over (partition by product_id order by menu_pos) as new_menu_pos
from
product_pos;
Window functions require MySQL 8.
Trying to sort rows from lowest to highest continually, or rather repeatedly using MySql. For example: if a column has the following values: 1,3,2,4,2,1,4,3,5, then it should end up like this 1,2,3,4,5,1,2,3,4. So it goes from lowest to highest, but tries to sort again from lowest to highest multiple times.
For large sets, the semi-JOIN operation (the approach in the answer from Strawberry) may create an unwieldy resultset. (Then again, MySQL may have some optimizations in there.)
Another alternative available in MySQL is to use "user variables", like this:
SELECT r.mycol
FROM ( SELECT IF(q.mycol=#prev,#seq := #seq + 1,#seq := 1) AS seq
, #prev := q.mycol AS mycol
FROM mytable q
JOIN (SELECT #prev := NULL, #seq := NULL) p
ORDER BY q.mycol
) r
ORDER BY r.seq, r.mycol
Let me unpack that a bit, and explain what it's doing, starting with the inner query (inline view aliased as r.) We're telling MySQL to get the column (mycol) containing the values you want to sort, e.g. 1,3,2,4,2,1,4,3,5 and we're telling MySQL to order these in ascending sequence: 1,1,2,2,3,3,4,4,5.
The "trick" now is to use a MySQL user variable, so that we can compare the mycol value from the current row to the mycol value from the previous row, and we use that to assign an ascending sequence value, from 1..n on each distinct value.
With that resultset, we can tell MySQL to order by that assigned sequence value first, and then by the value from mycol.
If there is a unique id on each row, then a correlated subquery can be used to get an equivalent result (although this approach is very unlikely to perform as well on large sets)
SELECT r.mycol
FROM mytable r
ORDER
BY ( SELECT COUNT(1)
FROM mytable q
WHERE q.mycol = r.mycol
AND q.id <= r.id
)
, r.mycol
Here's the setup for the test case:
CREATE TABLE mytable (id INT, mycol INT);
INSERT INTO mytable (id, mycol) VALUES
(1,1),(2,3),(3,2),(4,4),(5,2),(6,1),(7,4),(8,3),(9,5);
there is no order two time just this
ORDER BY column ASC
Let's pretend that the PK is a unique integer. Consider the following...
CREATE TABLE seq(id INT NOT NULL PRIMARY KEY,val INT);
INSERT INTO seq VALUES (8,1),(4,2),(1,3),(2,4),(7,0),(6,1),(3,2),(5,5);
SELECT * FROM seq ORDER BY val;
+----+------+
| id | val |
+----+------+
| 7 | 0 |
| 6 | 1 |
| 8 | 1 |
| 3 | 2 |
| 4 | 2 |
| 1 | 3 |
| 2 | 4 |
| 5 | 5 |
+----+------+
SELECT x.*
, COUNT(*) rank
FROM seq x
JOIN seq y
ON y.val = x.val
AND y.id <= x.id
GROUP
BY id
ORDER
BY rank
, val;
+----+------+------+
| id | val | rank |
+----+------+------+
| 7 | 0 | 1 |
| 6 | 1 | 1 |
| 3 | 2 | 1 |
| 1 | 3 | 1 |
| 2 | 4 | 1 |
| 5 | 5 | 1 |
| 8 | 1 | 2 |
| 4 | 2 | 2 |
+----+------+------+