Get sum of repeating items as an array of IDS - mysql

I have question regarding getting sum of the items by their IDS. Is there are any elegant way to do this with sql procedure? The IDS are coming from in array, for instance (10, 10, 11, 11, 12). So the sum should be 300. If I am right functions cannot take array as an parameter in mysql, so it can be procedure.
The closest what I could think of:
SELECT price FROM table WHERE FIND_IN_SET(ID, (10, 10, 11, 11, 12))
tho it doesn't work properly.
I know that SUM is almost does what I need, except it skips duplicate values, if there is a way to use it so it wouldn't skip, then it probably would be fastest. Table is below:
|---------------------|------------------|
| ID | Price |
|---------------------|------------------|
| 10 | 34 |
|---------------------|------------------|
| 11 | 99 |
|---------------------|------------------|
| 12 | 34 |
|---------------------|------------------|

You would create a derived table and join:
select sum(t.price)
from (select 10 as id union all
select 10 as id union all
select 11 as id union all
select 11 as id union all
select 12 as id
) i join
t
on i.id = t.id;

In MySQL 8.0, you can do this with running your input array thru JSON_TABLE, which will interpret your array entries as rows.
This approach respects the array's order and doesn't skip duplicities, as it's not a filtering function like WHERE.
set #arr = '[10, 10, 11, 11, 12]';
select *
from json_table(#arr, '$[*]' columns (id int path '$')) input
left join products using (id);
-> id price
10 34
10 34
11 99
11 99
12 34
select sum(price)
from json_table(#arr, '$[*]' columns (id int path '$')) input
left join products using (id);
-> 300

Related

checking multiple values in between two columns mysql

I am trying for a MySQL query to check multiple values between two columns values.
For example, for one value here is my query which works
SELECT column3
FROM table
WHERE (12 between minvaluecol AND maxvaluecol) AND
id = 123;
I would like to check multiple values like (12,13,14,67,68) and should return the values that are in between minvaluecol and maxvaluecol columns. In this case, only 12,13,14 are in between minvaluecol and maxvaluecol columns whereas 67,68 are not.
my table looks like this,
id | minvaluecol | maxvaluecol
---- | ----------- | ------------
121 | 23 | 35
123 | 10 | 20
125 | 40 | 50
output for id 123 should look like,
12 | true
13 | true
14 | true
67 | false
68 | false
Please help me with this query in MySQL. Thank you.
Update
Completely revamped the answer based on updated question.
As you need all the values as different rows, you need to SELECT all of them with UNION and LEFT JOIN it with the original table, e.g.:
SELECT a.val, IF(a.val BETWEEN tv.minvaluecol AND maxvaluecol, 'true', 'false') AS result
FROM (
SELECT 12 AS val
UNION
SELECT 13 AS val
UNION
SELECT 14 AS val
UNION
SELECT 67 AS val
UNION
SELECT 68 AS val) a
JOIN test_values tv
WHERE tv.id = 123;
Here's the SQL Fiddle.
The easiest way to get your result is to Insert those values into a table and then join like this:
SELECT value,
CASE WHEN value between minvaluecol AND maxvaluecol THEN 'true ELSE 'false' END
FROM table
CROSS JOIN table_with_values
WHERE id = 123
ORDER BY value

sql get couple of elements

I have the following sql table:
| ID | numbers |
|----|-----------------------------|
| 1 | 1,3,19,23,28,32,39,42,60,80 |
| 2 | 1,3,18,24,29,33,40,43,61,80 |
| 3 | 1,2,3,25,30,34,41,44,62,78 |
In Numbers I have a string with 10 numbers.
I want to get all couple of two elements (and if it is possible for three, four etc) in SQL Server or MySQL.
For example for two elements:
1,3 appers in all rows (3 times)
1, 80 appears in the first and second row (2 times)
etc
I tried to split numbers from every row and insert into a temporary table and after generate combinations of 10 choose k (where k is numbers of elements in a couple) but something doesn't work. I don't know if it's the best idea.
My code in this moment: http://pastebin.com/qRjPdfay
Thanks
Yes, splitting your numbers coulmns to rows would make things easier. If you are using MySQL you could use a query like this:
CREATE TABLE mytable2 AS
SELECT
ID, SUBSTRING_INDEX(SUBSTRING_INDEX(numbers, ',', n),',',-1) AS number
FROM
mytable CROSS JOIN (SELECT 1 AS n
UNION ALL SELECT 2 AS n
UNION ALL SELECT 3 AS n
UNION ALL SELECT 4 AS n
UNION ALL SELECT 5 AS n
UNION ALL SELECT 6 AS n
UNION ALL SELECT 7 AS n
UNION ALL SELECT 8 AS n
UNION ALL SELECT 9 AS n
UNION ALL SELECT 10 AS n) d;
(this will work if all numbers contains exactly 10 numbers an no less, if there are less this query needs some improvements). Then you can count the time each number appears:
SELECT number, COUNT(*) as appears
FROM mytable2
GROUP BY number
ORDER BY appears DESC
and you can group number by the number of times they appear:
SELECT
appears, GROUP_CONCAT(number) AS numbers
FROM (
SELECT number, COUNT(*) as appears
FROM mytable2
GROUP BY number
ORDER BY appears DESC
) g
GROUP BY
appears
ORDER BY
appears DESC
(MySQL only) and the result will be like this:
| appears | numbers |
|---------|---------------|
| 3 | 3,1 |
| 2 | 80 |
| 1 | 43,23,40..... |
Please see a fiddle here.

MySQL How can I add values of a column together and remove the duplicate rows?

Good day,
I have a MySQL table which has some duplicate rows that have to be removed while adding a value from one column in the duplicated rows to the original.
The problem was caused when another column had the wrong values and that is now fixed but it left the balances split among different rows which have to be added together. The newer rows that were added must then be removed.
In this example, the userid column determines if they are duplicates (or triplicates). userid 6 is duplicated and userid 3 is triplicated.
As an example for userid 3 it has to add up all balances from rows 3, 11 and 13 and has to put that total into row 3 and then remove rows 11 and 13. The balance columns of both of those have to be added together into the original, lower ID row and the newer, higher ID rows must be removed.
ID | balance | userid
---------------------
1 | 10 | 1
2 | 15 | 2
3 | 300 | 3
4 | 80 | 4
5 | 0 | 5
6 | 65 | 6
7 | 178 | 7
8 | 201 | 8
9 | 92 | 9
10 | 0 | 10
11 | 140 | 3
12 | 46 | 6
13 | 30 | 3
I hope that is clear enough and that I have provided enough info. Thanks =)
Two steps.
1. Update:
UPDATE
tableX AS t
JOIN
( SELECT userid
, MIN(id) AS min_id
, SUM(balance) AS sum_balance
FROM tableX
GROUP BY userid
) AS c
ON t.userid = c.userid
SET
t.balance = CASE WHEN t.id = c.min_id
THEN c.sum_balance
ELSE 0
END ;
2. Remove the extra rows:
DELETE t
FROM
tableX AS t
JOIN
( SELECT userid
, MIN(id) AS min_id
FROM tableX
GROUP BY userid
) AS c
ON t.userid = c.userid
AND t.id > c.min_id
WHERE
t.balance = 0 ;
Once you have this solved, it would be good to add a UNIQUE constraint on userid as it seems you want to be storing the balance for each user here. That will avoid any duplicates in the future. You could also remove the (useless?) id column.
SELECT SUM(balance)
FROM your_table
GROUP BY userid
Should work, but the comment saying fix the table is really the best approach.
You can create a table with the same structure and transfer the data to it with this query
insert into newPriceTable(id, userid, balance)
select u.id, p.userid, sum(balance) as summation
from price p
join (
select userid, min(id) as id from price group by userid
) u ON p.userid = u.userid
group by p.userid
Play around the query here: http://sqlfiddle.com/#!2/4bb58/2
Work is mainly done in MSSQL but you should be able to convert the syntax.
Using a GROUP BY UserID you can SUM() the Balance, join that back to your main table to update the balance across all the duplicates. Finally you can use RANK() to order your duplicate Userids and preserve only the earliest values.
I'd select all this into a new table and if it looks good, deprecate your old table and rename then new one.
http://sqlfiddle.com/#!3/068ee/2

selecting the ID that fulfils multiple clauses

It's hard to put in correctly, but I'm using MySQL and I need to select one id, let's call it parent_id which has to meet child_id values in multiple rows.
For example:
+-----------+----------+
| parent_id | child_id |
+-----------+----------+
| 1 | 10 |
+-----------+----------+
| 2 | 11 |
+-----------+----------+
| 2 | 12 |
+-----------+----------+
| 3 | 13 |
+-----------+----------+
| 4 | 11 |
+-----------+----------+
| 5 | 12 |
+-----------+----------+
Now if I pass child_id parameters 11 and 12, I have to get parent_id 2 back, but if I pass 10 and 11, I have to get nothing back. Also, if I pass 11, I have to receive 4. And if I pass 13, I have to receive 3 back.
How do I go about this? I tried counting the parent_id's and using HAVING clause, also using GROUP BY clause, but nothing I try meets all of my requirements.
EDIT:
Example Fiddle: http://sqlfiddle.com/#!2/abbc4/5
EDIT2:
Expected results:
Passed parameters: 11, 12
Received result: 2
Passed parameters: 11
Received result: 4
Passed parameters: 13
Received result: 3
Passed parameters: 12, 13
Received result NULL
EDIT3:
Updated the spec. See here also: http://sqlfiddle.com/#!2/2f750/1
The following statement does what you want. I am not so sure about its performance though...
select t.parent_id, t.cnt from
(
select parent_id, count(*) cnt
from t
WHERE child_id IN (12, 11)
GROUP BY parent_id
) t
inner join
(
select parent_id, count(*) cnt
from t group by parent_id
) s
on t.parent_id = s.parent_id
and t.cnt = s.cnt -- Check that the parent has exactly as many children as
-- passed in - and not more. Prevents matching if only part
-- of the children of a parent were specified.
and t.cnt = 2 -- Check that all passed in children produced a match on the same
-- parent. Prevents matching of parents that match only a subset
-- of the specified children
Replace the 2 with the number of specified children in the IN list.
You can also use this more compact version
select case
when min(t.parent_id) = max(t.parent_id) -- parent_ids are the same?
-- and all children share the same parent?
and count(t.parent_id) = (
select count(parents.parent_id)
from t parents
where parents.parent_id in
(select parent_id
from t
where child_id in (11, 12) -- <= change here
))
then t.parent_id
else null
end as parent_id
from t
where child_id in (11, 12); -- <= and here
I have tested this and works for all your use cases
You can test here http://sqlfiddle.com/#!2/abbc4/183
You have to have two variables for this to work. First one is the comma separated list of your child_ids (child_list) and second is the number of the children (the count of children in your child_list) you are searching for (child_count).
SELECT parent_id,COUNT(*)
FROM table
WHERE child_id IN (<child_list>)
GROUP BY parent_id
HAVING COUNT(*)=<child_count>
This should give you the desired results.

Is there a way to include duplicates from MySQL query: select..from..where..id in (list)

I am trying to get the results for the query type
SELECT * FROM table WHERE id IN(2,4,6,1,1,2) ORDER BY field (id,2,4,6,1,1,2)
and I want to get results in the same order as the list including : the duplicates. The above query retains the order but cuts out duplicates. I know I can post-process the results but just wondering if there is an easier way.
Thanks
This will actually achieve what you want:
SELECT * FROM table
inner join (
select 1 as sort, 2 as value union all
select 2, 4 union all
select 3, 6 union all
select 4, 1 union all
select 5, 1 union all
select 6, 2) X on X.value=table.id
ORDER BY X.sort
How are you building the query? If you're not opposed to doing a little manual work (which you could later wrap in some code), unions should get you what you need:
select id from table where id in (1, 2, 4, 6)
union all
select id from table where id in (6, 8);
Returns:
------
| id |
|====|
| 1 |
| 2 |
| 4 |
| 6 |
| 6 |
| 8 |
------
EDIT: Actually, I don't think that helps your ordering, though. Let me play with this some more.