How sum certain duplicated data? - mysql

I have a list of ids, some ids can be duplicated.
How to sum values for the same id in one query?
SELECT SUM(`power`)
FROM `stats`
WHERE `template` IN (489191, 489191, 489100)
The template 489191 has power = 1 and 489100 has power = 0
so I need to get 2 as the result.

You need to join with table value data to get the desired number of rows e.g.:
select sum(stats.power)
from (
select 489191 as template union all
select 489191 union all
select 489100
) as vlist
join stats on vlist.template = stats.template
If you're using MySQL 8 or later you can use the values table value constructor instead of union all:
select sum(stats.power)
from (values
row(489191),
row(489191)
row(489100)
) as vlist(template)
join stats on vlist.template = stats.template

Related

Query for selecting duplicates + finding lowest value

I have 3 tables in 3 different databaes; Currently the goal here is to find the duplicates unique ID in the three databases and then find the lowest price value of the duplicates unique ID.
Currently I'm using a INNER JOIN to query between only 2 database... Can anyone advise on how to add the third one?
set #a = (SELECT db1.tb1.var1 from db1.tb1
INNER JOIN db2.tb1 ON db2.tb1.var1 = db1.tb1.var1
UNION );
Also, once I have the #a variable set to the duplicate, I wanted to grab a secondary value here.
SELECT price
FROM db1.tb1
WHERE asin=#a
UNION ALL
SELECT price
FROM db2.tb1
WHERE asin=#a
UNION ALL
SELECT price
FROM db3.tb1
WHERE asin=#a
However, the result I'd get would return multiple rows (obviously), How do I query only for the MIN() number from this ?
Any help is appreciated.
Thanks,
Put your query into a subquery, and then use MIN() in the main query.
SELECT MIN(price)
FROM (
SELECT price
FROM db1.tb1
WHERE asin=#a
UNION ALL
SELECT price
FROM db2.tb1
WHERE asin=#a
UNION ALL
SELECT price
FROM db3.tb1
WHERE asin=#a) AS x
You can use order by and limit:
SELECT price
FROM db1.tb1
WHERE asin = #a
UNION ALL
SELECT price
FROM db2.tb1
WHERE asin = #a
UNION ALL
SELECT price
FROM db3.tb1
WHERE asin = #a
ORDER BY price
LIMIT 1;

MySQL Count Of Field Value

I have a MySQL view with the fields id and set. Because it's a view, most ids are repeated and have duplicate entries. For example, and id = 120158 may have 5 rows, 3 where set = A and 2 where set = B. I want to run a query off of the view to display the number of rows each id has associated with its corresponding sets.
I tried:
SELECT `id`,
`set`,
(SELECT COUNT(set)) AS `CountOfSet`
FROM `view1`
However, this simply returns the same view (duplicate rows still exist) with CountOfSet equal to 1 for every row. Any ideas?
You should be grouping your results by id and set to get the desired result:
SELECT `id`, `set`, COUNT(*) AS `CountOfSet` FROM `view1`
GROUP BY `id`, `set`
This would return the results as
120158 A 3
120158 B 2
You need to group your data using GROUP BY clause:
GROUP BY `id`, `set`
Composing all together:
SELECT `id`,
`set`,
COUNT(*) AS `CountOfSet`
FROM `view1`
GROUP BY `id`, `set`

Force MySQL to return a result for all option within IN

I have a simple MySQL statement:
SELECT q1, COUNT(q1) FROM results WHERE q1 IN ('1','2','3');
Currently there are only results for 1 and 3 - results are:
1 = 6
3 = 7
But what I need is for MySQL to bring back a result for 1,2 and 3 even though 2 has no data, as this:
1 = 6
2 = 0
3 = 7
Any ideas?
This is tricky because no rows match your value (2), they cannot be counted.
I would solve this by creating a temp table containing the list of values I want counts for:
CREATE TEMPORARY TABLE q ( q1 INT PRIMARY KEY );
INSERT INTO q (q1) VALUES (1), (2), (3);
Then do an OUTER JOIN to your results table:
SELECT q.q1, COALESCE(COUNT(*), 0) AS count
FROM q LEFT OUTER JOIN results USING (q1)
GROUP BY q.q1;
This way each value will be part of the final result set, even if it has no matching rows.
Re comment from #Mike Christensen:
MySQL doesn't support CTE's, in spite of it being requested as far back as 2006: http://bugs.mysql.com/bug.php?id=16244
You could do the same thing with a derived table:
SELECT q.q1, COALESCE(COUNT(*), 0) AS count
FROM (SELECT 1 AS q1 UNION ALL SELECT 2 UNION ALL SELECT 3) AS q
LEFT OUTER JOIN results USING (q1)
GROUP BY q.q1;
But this creates a temp table anyway.
A SQL query doesn't really have a way to refer to the values in your IN clause. I think you'd have to break this down into one query for each value. Something like:
SELECT 1 as q1, COUNT(1) FROM results WHERE q1 = '1'
UNION ALL
SELECT 2 as q1, COUNT(1) FROM results WHERE q1 = '2'
UNION ALL
SELECT 3 as q1, COUNT(1) FROM results WHERE q1 = '3'
Fiddle
Note: If there are a lot of values in your IN clause, you might be better off to write your code in a way where missing values are assumed to have zero.
In general, you cannot query something that does not exists. So, you must create data for it. Use union to add those missing data values.
select q1, COUNT(*)
from results
where q1 in ('1','2','3')
group by q1
union
select q1, 0
from (
select '1' as q1
union
select '2'
union
select '3'
) as q
where q1 not in (
select q1
from results
)

Inserting duplicate records based on a value without using cursor

I had a problem in database. I have to insert duplicate records of a particular record on a another table based on a value.
First i used cursor to fetch each records and get the number of duplication i wants and after that used another cursor for duplication. Everything worked fine. But if the records in more than 500, i went dead slow. Then i did some research and found a way to insert without cursor.
INSERT INTO report(id, Name)
SELECT i.id,i.Name FROM (SELECT 1 AS id
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8
UNION SELECT 9
UNION SELECT 10) AS o
INNER JOIN table i WHERE o.id<=i.frequence;
where frequence is the number of duplication. Please drop your idea to improve your query.
You could try creating a table with a record for each value from 1 to 10 and then join to that. I'm not sure it would be any faster though. You would have to experiment with it.
In this example the table with the values from 1 to 10 is called "dup" and the field containing these values is called "id".
INSERT INTO report(id, Name)
SELECT i.id, i.Name
FROM table i
JOIN dup d
ON d.id <= i.frequence;
If you have any table that contains a row number that goes at least as high as the maximum frequence, you could to this:
INSERT INTO report(id, Name)
SELECT i.id,i.Name FROM table i
inner join (
select distinct some_row_number_column from some_table
) o on o.some_row_number_column <= i.frequence;
This is basically the same as what you were doing, but it avoids the messy union all statements.
Or you could make a cursor that inserts numbers from 1 to the maximum frequence into a temporary table, then use that in your join. Or you could use a row numbering variable to generate the necessary sequence. Basically, do anything that will generate a list of consecutive numbers from 1 to the maximum that you need.
I would normally use recursion for this (DB2 syntax):
INSERT INTO report(id, Name)
with num_list (num) as (
values (1)
union all
select num + 1 from num_list
where num < (select max(frequence) from table)
)
SELECT i.id,i.Name FROM table i
inner join num_list on num_list.num <= i.frequence;
However, MySQL doesn't support recursion, apparently.

mysql select rows with same ids and preserve their order?

just a quick question:
i have to have one single query that has multiple rows - some rows are identicle - and the order of rows must be preserved in the result -
some idea of what im refering to:
SELECT id,date
FROM items
WHERE id IN (1,2,1,3)
ORDER BY id=1 DESC,id=2 DESC,id=1 DESC,id=3 DESC;
unfortunately mysql result is this:
1,2,3
not 1,2,1,3
it removes the duplicate which i have to have in my result to display in multiple panels on the same webpage -
i really dont want to loop thru each id one by one to get them the way i want to display -
is there a way to actually have one single query that will preserve the order and pull out rows based on request whether its unique or not -
Your query as it stands will never work, because duplicate values in a list of values of an IN clause are ignored. The only way to make this work is by using UNION ALL:
SELECT id, date FROM items where id = 1
UNION ALL
SELECT id, date FROM items where id = 2
UNION ALL
SELECT id, date FROM items where id = 1
UNION ALL
SELECT id, date FROM items where id = 3;
But to be frank, I suspect your data model so far past screwed it's unusable.
try
SELECT
id,
date
FROM items
WHERE id IN (1,2,1,3)
ORDER BY FIND_IN_SET(id, '1,2,1,3')
Another scrupulous way to answer a suspicious question:
SELECT
items.id,
items.date
FROM
items
JOIN
( SELECT 1 AS id, 1 AS ordering
UNION ALL
SELECT 2, 2
UNION ALL
SELECT 1, 3
UNION ALL
SELECT 3, 4
) AS auxilary
ON
auxilary.id = items.id
ORDER BY
auxilary.ordering
Another approach (untested, but should give you the idea):
CREATE TEMPORARY TABLE tt (id INT, ai int unsigned auto_increment primary key);
INSERT INTO tt (id) VALUES (1), (2), (1), (3);
SELECT
id,
date
FROM items JOIN tt USING (id)
ORDER BY tt.ai;
keeps the given order.
If you want to include the records with id=1 and the order doesn't matter as long as you get them, you can split your query into two queries, one for (1,2,3) union all the other query for id=1 or just do:
... In (1,2)
Union all
... In (1,3)
Example:
Select * from
(Select case id when 1 then 1 when 2 then 2 as pseudocol, othercolumns
From table where Id in (1,2)
Union all
Select case id when 1 then 3 when 3 then 4 as pseudocol, othercolumns
From table where Id in (1,3)) t order by pseudocol
Instead of doing what you are trying to, just select the unique rows you need. In the frontend code, store each unique row once in a key=>value structure, where key is the item ID and value is whatever data you need about that item.
Once you have that you can use frontend logic to output them in the desired order including duplicates. This will reduce the amount of redundant data you are trying to select.
For example This is not usable code - exact syntax required depends on your scripting language
-- setup a display order
displayOrder= [1,2,1,3];
-- select data from database, order doesn't matter here
SELECT id,date
FROM items
WHERE id IN (displayOrder);
-- cache the results in a key=> value array
arrCachedRows = {};
for (.... each db row returned ...) {
arrCachedRows[id] = date;
}
-- Now output in desired order
for (listIndex in displayOrder) {
-- Make sure the index is cached
if (listIndex exists in arrCachedRow) {
echo arrCachedRows[listIndex ];
}
}
If you must persist in using UNION despite my warnings
If you go against the above recommendation and absolutely MUST have them back in 1 query in that order then add on an additional row which will enforce the row order. See below query where I use variable #subIndex to add an incrementing value as subIndex. This in turn lets you reorder by that and it'll be in the requested order.
SELECT
i.*
FROM (
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 1
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 2
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 1
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, id, date FROM items where id = 3
) AS i,(SELECT #subIndex:=0) v
ORDER BY i.subIndex
Or a slightly cleaner version that keeps item selection until the outside and hides the subindex
SELECT
items.*
FROM items
-- initialise variable
INNER JOIN (SELECT #subIndex:=0) v
-- create a meta-table with the ids desired in the order desired
INNER JOIN (
SELECT #subIndex:=#subIndex+1 AS subIndex, 1 AS id
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, 2 AS id
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, 1 AS id
UNION
SELECT #subIndex:=#subIndex+1 AS subIndex, 3 AS id
) AS i
ON i.id = items.id
-- order by the subindex from i
ORDER BY i.`subIndex` ASC