mysql multiple similar table query[seems not duplicate] - mysql

I have a sales software that use MYSQL database and i want to make a web extension using PHP. the software creating a new table everyday for every days transaction with same column but name like sales_data_Yearmonthday.
e.g
sales_data_20190122
sales_data_20190123
sales_data_20190124
sales_data_20190125
sales_data_20190126
sales_data_20190127
sales_data_20190128
So my question is what will be the best way to query these table if i want to get sales report for last 7 days?
UNION ALL is one option to join all table but are there any other option to do that for getting best performance as 356 table will be created every year and each table may have contain over 5000 records.
its may not the best database design but i cannot change it.

Given the specified constraints (as unfortunate as that situation is)...
using UNION ALL is the most appropriate solution to satisfying the specification.
If we are wanting the "past seven days", then we (our code) needs to figure out which tables will be required (vs the tables that would be required e.g. "so far this month") and dynamically construct SQL text.
We first write the query against one table, get that tested.
SELECT t.fee
, t.fi
, t.fo
, t.fum
FROM sales_data_20190128 t
WHERE t.foo = ?
Then we just repeat that query for each table that might included rows we are interested in, excluding tables we know for sure will not contain rows we want, and combine the queries with UNION ALL set operator.
If we need the whole set ordered, then wrap each SELECT in parens, and finish with an ORDER BY clause. e.g.
( SELECT t.fee, t.fi, t.fo, t.fum
FROM sales_data_20190124 t
WHERE t.foo = ?
)
UNION ALL
( SELECT t.fee, t.fi, t.fo, t.fum
FROM sales_data_20190125
WHERE t.foo = ?
)
UNION ALL
( SELECT t.fee, t.fi, t.fo, t.fum
FROM sales_data_20190126 t
WHERE t.foo = ?
)
UNION ALL
( SELECT t.fee, t.fi, t.fo, t.fum
FROM sales_data_20190127 t
WHERE t.foo = ?
)
UNION ALL
( SELECT t.fee, t.fi, t.fo, t.fum
FROM sales_data_20190128 t
WHERE t.foo = ?
)
ORDER BY 1,2
Do NOT try to simplify the code by creating view that concatenates all of the tables together, and query against that. Don't do that.

Related

SQL - Nested query optimization

How can I optimize this query SQL?
CREATE TABLE table1 AS
SELECT * FROM temp
WHERE Birth_Place IN
(SELECT c.DES_COM
FROM tableCom AS c
WHERE c.COD_PROV IS NULL)
ORDER BY Cod, Birth_Date
I think that the problem is the IN clause
First of all it's not quite valid SQL, since you are selecting and sorting by columns that are not part of the group. What you want to do is called "select top N in group", check out Select first row in each GROUP BY group?
Your query doesn't make sense, because you have SELECT * with GROUP BY. Ignoring that, I would recommend writing the query as:
SELECT t.*
FROM temp t
WHERE EXISTS (SELECT 1
FROM tableCom c
WHERE t.Birth_Place = c.DES_COM AND
c.COD_PROV IS NULL
)
ORDER BY Cod, Birth_Date;
For this, I recommend an index on tableCom(desc_com, cod_prov). Your database might also be able to use an an index on temp(cod, birth_date, birthplace).

Refactoring a large union statement to use a SELECT which breaks early

It's a bit difficult to explain the situation, but currently I'm generating massive unions to accomplish this. They look at bit like:
(
SELECT
ipaddress
FROM post
WHERE ipaddress = 'someipaddress'
AND userid NOT IN (1, {$postinfo['userid']}, {$vbulletin->options['sdwikipostuserid']})
LIMIT 1
)
UNION
(
SELECT
ipaddress
FROM post
WHERE ipaddress = 'someotheripaddress'
AND userid NOT IN (1, {$postinfo['userid']}, {$vbulletin->options['sdwikipostuserid']})
LIMIT 1
)
These get huge fast, but seem to be the fastest way for me to accomplish this right now. I've tried refactoring it to something like:
SELECT
ipaddress
FROM post
WHERE ipaddress in ('all ips', .....)
AND userid NOT IN (1, {$postinfo['userid']}, {$vbulletin->options['sdwikipostuserid']})
GROUP BY ipaddress
But this is around x5 slower than the massive union statement. The big issue is that the post table is huuuuuge, so the refactored SQL is forced to look through the entire table where each union statement can break after finding a single instance. Is there any way to specify the SQL to break on finding the first unique group?
Anyone have tips on how to refactor the huge union statement above into something cleaner?
You can write the query like this:
select i.ipaddress
from (select 'someipaddress' as ipaddress union all
select 'someotheripaddress'
) i
where exists (select 1
from posts p
where p.ipaddress = i.ipaddress and
p.userid NOT IN (1, {$postinfo['userid']}, {$vbulletin->options['sdwikipostuserid']})
);
This is optimized with an index on posts(ipaddress, userid) -- one index, two columns.

Is there any way to execute a SELECT only if a condition in a different table is met?

Is there any way to do that in a single query? Or do I have to manage it externally? It is not a JOIN of any kind.
SELECT
IF (
(SELECT indicator FROM configuration_table) = 1,
(SELECT series_id FROM series_table LIMIT 1),
''
) as if_exp
FROM
series_table
This executes but returns the first ID over and over, and if I take out the LIMIT 1, it doesn't work as it expects only one result. But what I need is that, if this condition is met:
(SELECT indicator FROM configuration_table) = 1,
Then I need all this data returned:
SELECT series_id, series_code, series_name FROM series_table
Is it possible somehow? Should I be doing two queries and managing the data from php? Thank you very much.
The easiest way would be:
IF ((SELECT indicator FROM configuration_table) = 1) THEN
SELECT series_id, series_code, series_name FROM series_table
END IF
You did not show us what to do, when the condition is false. We do not know the relationship between configuration_table and series_table, so we can't find a way to make it in a single query.
I have copied this answer from IF Condition Perform Query, Else Perform Other Query this answer.
SELECT CASE WHEN ( (SELECT indicator FROM configuration_table) = 1 )
THEN
SELECT series_id, series_code, series_name FROM series_table
ELSE
<QUERY B>
END
Here Query B should replaced by your desired query.

SQL Union All alias from first table?

I have two tables
[data] - title,maker,partnum,price
[cross] - product(data.partnum),title,maker,partnum,price
What I want is listing all product via sysn number. How can I get with union all data like that with ordering ->
[data table] Microsoft, "some note", 9989, $20
[cross table] Microsoft reseller, "some note", 1045, $30
[cross table] Apple reseller in Microsoft :), "some note", 2233, $40
virtual spacer :)
[data table] Microsoft, "some note", 9989, $10
[cross table] Lenovo reseller in Microsoft..
Im trying with this
SELECT `title`,'Microsoft' AS `maker`,`partnum`,`price`
FROM data as d
WHERE sysn=%s
GROUP BY partnum
UNION ALL
SELECT `title`,`maker`,`partnum`,`price`
FROM cross as c
WHERE c.product=d.partnum
GROUP BY `partnum`
Thanks
It's not entirely clear what you are asking. Consider setting up an example schema and data in http://sqlfiddle.com.
A "virtual spacer" is going to be very messy to achieve. It's not totally impossible, just messy.
For returning rows in a particular sequence, you are going to need to add an ORDER BY clause.
It's not at all clear why you are including GROUP BY clauses. The GROUP BY on the first SELECT would effectively "collapse" example output rows 1. and 5. given the same value 9989 for partnum. Absent an explanation of why you need a GROUP BY, I'm going to guess that what you really intended was an ORDER BY clause.
It's also not clear why the literal value 'Microsoft' would need to appear in the SELECT list, in place of the maker column from the data table. There's nothing invalid SQL-wise with doing that. But absent an explanation, it just doesn't make much sense.
The question we should to ask... Why is this specific result needed? Is there a different result which would be easier to achieve, which would equally satisfy the requirements?
Setting aside the "virtual spacer" row, the return from a query like this seems like it satisfies most of the specification:
SELECT t.src
, t.title
, t.maker
, t.partnum
, t.price
FROM (
SELECT '[data]' AS `src`
, d1.title AS `title`
, d1.maker AS `maker`
, d1.partnum AS `partnum`
, d1.price AS `price`
, d1.partnum AS `product`
FROM data d1
WHERE d1.sysn = ?
UNION ALL
SELECT '[cross]'
, c2.title
, c2.maker
, c2.partnum
, c2.price
, c2.product
FROM cross c2
JOIN data d2
ON d2.partnum = c2.product
WHERE d2.sysn = ?
)
ORDER BY t.product DESC, t.src DESC, t.price ASC
If you are using MySQL, "cross" is a reserved word and you must enclose cross in backticks to use it as a table name (The backtick is next the the '1' on the keyboard on the upper left).

Get a list of ids not present in a table

I have a list of ids, and I want to query a mysql table for ids not present in the table.
e.g.
list_of_ids = [1,2,4]
mysql table
id
1
3
5
6
..
Query should return [2,4] because those are the ids not in the table
since we cant view ur code i can only work on asumption
Try this anyway
SELECT id FROM list_of_ids
WHERE id NOT IN (SELECT id
FROM table)
I hope this helps
There is a horrible text-based hack:
SELECT
substr(result,2,length(result)-2) AS notmatched
FROM (
SELECT
#set:=replace(#set,concat(',',id,','),',') AS result
FROM (
select #set:=concat(',',
'1,2,4' -- your list here
,',')
) AS setinit,
tablename --Your tablename here
) AS innerview
ORDER BY LENGTH(result)
LIMIT 1;
If you represent your ids as a derived table, then you can do this directly in SQL:
select list.val
from (select 1 as val union all
select 2 union all
select 4
) list left outer join
t
on t.id = list.val
where t.id is null;
SQL doesn't really have a "list" type, so your question is ambiguous. If you mean a comma separated string, then a text hack might work. If you mean a table, then something like this might work. If you are constructing the SQL statement, I would advise you to go down this route, because it should be more efficient.