Slow query on update using select count(*) - mysql

I have to count how many times a number from table2 occurs between the number in range table2.a and table2.b
i.e. we wanna know how many times we have this : a < start < b
I ran the following query :
UPDATE table2
SET occurrence =
(SELECT COUNT(*) FROM table1 WHERE start BETWEEN table2.a AND table2.b);
table2
ID a b occurrence
1 1 10
2 1 20
3 1 25
4 2 30
table1
ID start col1 col2 col3
1 1
2 7
3 10
4 21
5 25
6 27
7 30
table2 as
3 indexes on a, b and occurrence
1567 rows (so we will SELECT COUNT(*) over table2 1567 times..)
ID column as PK
table1 as
1 index on start
42,000,000 rows
Column start was "ordered by column start"
ID column as PK
==> it took 2.5hours to do 2/3 of it. I need to speed this up... any suggestions ? :)

You could try to add the id column to the index on table 1:
CREATE INDEX start_index ON table1 (start,id);
And rewrite the query to
UPDATE table2
SET occurrence =
(SELECT COUNT(id) FROM table1 WHERE start BETWEEN table2.a AND table2.b);
This is called "covering index": http://www.simple-talk.com/sql/learn-sql-server/using-covering-indexes-to-improve-query-performance/
-> The whole query on table 1 can be served through the data in the index -> no additional page lookup for the actual record.

Use a stored procedure. Keep the result from COUNT in a local variable, then use it to run the UPDATE query.

I will do this
// use one expensive join
create table tmp
select table2.id, count(*) as occurrence
from table1
inner join table1
on table1.start between table2.a and table2.b
group by table1.id;
update table2, tmp
set table2.occurrence=tmp.occurrence
where table2.id=tmp.id;

I think count(*) makes the database read the data rows when in your case it only needs to read the index. Try:
UPDATE table2
SET occurrence =
(SELECT COUNT(1) FROM table1 WHERE start BETWEEN table2.a AND table2.b);

Related

select only when different value

I have this column: name and price. I don't really know how or why in mysql database there are few line that are double record exactly from the previous line.
how to select all records but show only one of the records if the record is double with a line in front or behind it?
For example I have this records:
id
name
price
1
book
5
2
lamp
7
3
lamp
7
4
book
5
5
book
5
the result I want is:
id
name
price
1
book
5
2
lamp
7
4
book
5
If you want to exclude rows that match the previous name, there are several ways like the following.
Case 1:
If you use MySQL8, you can use the LAG function.
SELECT t1.id,t1.name,t1.price FROM (
SELECT t2.id,t2.name,t2.price,
LAG(t2.name) OVER(ORDER BY t2.id) prev
FROM mytable t2
) t1
WHERE t1.prev IS NULL OR t1.name<>t1.prev
ORDER BY 1
Case 2:
If the ids are continuous without any steps, you will get the expected result by comparing name and the previous id by JOIN.
SELECT t1.id,t1.name,t1.price FROM mytable t1
LEFT JOIN mytable t2
ON t1.name=t2.name AND
t1.id=t2.id-1
WHERE t1.id=1 OR t2.id IS NOT NULL
ORDER BY 1
Case 3:
If the ids are not continuous, there is a way to get the maximum id that does not exceed the other id.
SELECT t1.id,t1.name,t1.price FROM mytable t1
LEFT JOIN mytable t2
ON t1.name=t2.name AND
t1.id=(SELECT MAX(t3.id) FROM mytable t3 WHERE t3.id<t2.id)
WHERE t1.id=1 OR t2.id IS NOT NULL
ORDER BY 1
DB Fiddle
Select distinct is not an option here as id column is always unique. I guess this will work for you:
select min(id), name, price from table_name group by name, price

Select Distinct results from tow tables mysql

I have tow tables (MYSQL) contains different column names and want to query all the data from table 1 and all the data from table 2 which is not existing in table 1
the data is like:
Table 1
Ref Desc Price
A TEXT1 12
B TEXT2 10
C TEXT3 5
Table 2:
Code Desc Price
A TEXT1 7
B TEXT2 10
D TEXT4 2
I want the result to be like this:
Ref Desc Price
A TEXT1 12
B TEXT2 10
C TEXT3 5
D TEXT4 2
So i have tried to make this view:
CREATE OR REPLACE VIEW `partsquery` AS
SELECT table1.Ref AS reference,
table1.Desc AS description,
table1.Price AS price
FROM table1
UNION ALL
SELECT t2.code AS Ref,
t2.Desc AS description,
t2.price AS price
FROM
table2 AS t2
LEFT JOIN table1 AS t1
ON
t2.Code = t1.Ref
WHERE t1.Ref Is Null;
this view gives me what i want but actually its to slow because i have big data. So is there another way to get my result ?!
Use union all:
select t1.*
from table1 t1
union all
select t2.*
from table2 t2
where not exists (select 1 from table1 t1 where t1.code = t2.code);
For performance, you want in index on table1(code).
I cannot think of a faster way to run this query.
If you need this data quickly, you might need to create a second table, along with an trigger on the first two tables. The trigger would insert new rows according to your conditions. That will get rid of the union all and allow you to build indexes, at the cost of more code complexity and space usage.

Delete duplicates where field1 and field2 are the identical

I have a table like
productId retailerId
1 2
1 2
1 4
1 6
1 8
1 8
2 3
2 6
2 6
Now, I need to remove the duplicates. I've figured out how to remove duplicates when one field is the same. But I need to remove the duplicates such as 1 2, 1 8 and 2 6, where both fields are identical.
Any help would be very gratefully received.
Use mysql's multiple-table DELETE syntax as follows:
delete mytable
from mytable
join mytable t
on t.productId = mytable.productId
and t.retailerId = mytable.retailerId
and t.id < mytable.id
See this running on SQLFiddle.
Note that I have assumed that you have an id column as well.
Edit:
Since there is no id column, there simplest approach is to copy the desired data to a temporary table, delete all data, then copy it back, as follows:
CREATE TEMPORARY TABLE temptable
SELECT DISTINCT productId, retailerId
FROM mytable;
DELEYE FROM mytable;
INSERT INTO mytable
SELECT *
FROM temptable;

Adding one extra row to the result of MySQL select query

I have a MySQL table like this
id Name count
1 ABC 1
2 CDF 3
3 FGH 4
using simply select query I get the values as
1 ABC 1
2 CDF 3
3 FGH 4
How I can get the result like this
1 ABC 1
2 CDF 3
3 FGH 4
4 NULL 0
You can see Last row. When Records are finished an extra row in this format
last_id+1, Null ,0 should be added. You can see above. Even I have no such row in my original table. There may be N rows not fixed 3,4
The answer is very simple
select (select max(id) from mytable)+1 as id, NULL as Name, 0 as count union all select id,Name,count from mytable;
This looks a little messy but it should work.
SELECT a.id, b.name, coalesce(b.`count`) as `count`
FROM
(
SELECT 1 as ID
UNION
SELECT 2 as ID
UNION
SELECT 3 as ID
UNION
SELECT 4 as ID
) a LEFT JOIN table1 b
ON a.id = b.id
WHERE a.ID IN (1,2,3,4)
UPDATE 1
You could simply generate a table that have 1 column preferably with name (ID) that has records maybe up 10,000 or more. Then you could simply join it with your table that has the original record. For Example, assuming that you have a table named DummyRecord with 1 column and has 10,000 rows on it
SELECT a.id, b.name, coalesce(b.`count`) as `count`
FROM DummyRecord a LEFT JOIN table1 b
ON a.id = b.id
WHERE a.ID >= 1 AND
a.ID <= 4
that's it. Or if you want to have from 10 to 100, then you could use this condition
...
WHERE a.ID >= 10 AND
a.ID <= 100
To clarify this is how one can append an extra row to the result set
select * from table union select 123 as id,'abc' as name
results
id | name
------------
*** | ***
*** | ***
123 | abc
Simply use mysql ROLLUP.
SELECT * FROM your_table
GROUP BY Name WITH ROLLUP;
select
x.id,
t.name,
ifnull(t.count, 0) as count
from
(SELECT 1 AS id
-- Part of the query below, you will need to generate dynamically,
-- just as you would otherwise need to generate 'in (1,2,3,4)'
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
) x
LEFT JOIN YourTable t
ON t.id = x.id
If the id does not exist in the table you're selecting from, you'll need to LEFT JOIN against a list of every id you want returned - this way, it will return the null values for ones that don't exist and the true values for those that do.
I would suggest creating a numbers table that is a single-columned table filled with numbers:
CREATE TABLE `numbers` (
id int(11) unsigned NOT NULL
);
And then inserting a large amount of numbers, starting at 1 and going up to what you think the highest id you'll ever see plus a thousand or so. Maybe go from 1 to 1000000 to be on the safe side. Regardless, you just need to make sure it's more-than-high enough to cover any possible id you'll run into.
After that, your query can look like:
SELECT n.id, a.*
FROM
`numbers` n
LEFT JOIN table t
ON t.id = n.id
WHERE n.id IN (1,2,3,4);
This solution will allow for a dynamically growing list of ids without the need for a sub-query with a list of unions; though, the other solutions provided will equally work for a small known list too (and could also be dynamically generated).

find the output mysql query

I have a table name T1 having only one Column name Col1 having rows –
Col1
a
b
c
And another table name T2 also having only one Column name Col1 having rows –
Col1
x
y
z
Now I want record like
Col1--Col2
a------x
b------y
c------z
I am using mysql.
Thanks in advance!!
create table T1(col1 varchar(10));
insert T1 values ('a'),('b'),('c');
create table T2(col2 varchar(10));
insert T2 values ('x'),('y'),('z');
select A.col1, B.col2 from
(select #r:=#r+1 rownum, col1 from (select #r:=0) initvar, T1) A,
(select #s:=#s+1 rownum, col2 from (select #s:=0) initvar, T2) B
where A.rownum=B.rownum
Because there is no ORDER BY clause, you are depending on luck and convention for the row numbering to be according to the order inserted. It may not always be the case.
In your example, if you want to join the tables to get row results like this:
Row 1 - A,X
Row 2 - B,Y
Row 3 - C,Z
..then you will have to add a common field that you can JOIN the two tables on.
If you want to be able to return results from both tables like this:
Row 1 - A
Row 2 - B
Row 3 - C
Row 4 - X
Row 5 - Y
Row 6 - Z
.. then you will need to use a UNION:
(SELECT Col1 FROM T1) UNION (SELECT Col1 FROM T2)