Calculated Fields in MYsql - mysql

Excuse the "newbee to modern Databases" question in advance. But have a very simple question.
Given the following table:
ID Cost_Goods Sales_Price
1 100 150
2 75 95
3 500 700
I know that I can Query
SELECT Cost_Goods FROM someTable WHERE ID =2;
Very often I would like to know 'profit' where Profit is calculated as in:
SELECT profit FROM someTable WHERE ID =2;
I know that I can use a function here. But I have some limitations form a whole lot of legacy code. I wish to cmd up with a way to have a sort of "Calculation Field" in the database itself. That would, in addition to the above "profit" query allow me to query like.
SELECT Cost_Goods, profit FROM someTable WHERE ID =2;
Or Even:
SELECT * FROM someTable ORDER BY profit;
Is this possible?
I understand that there are more elegant ways to hand the calculation, but the interface from the application that I am working on/in simply don't allow anything other than avery simple query. I am attempting to shift the calculation over.

You can either alter the table to add in a column, maybe ideal maybe not, but this would require that the new field be kept up-to-date on each Insertion. Not a problem if you have control over this, but can come back and bite you if you think you updated all your INSERTS when in fact you haven't.
I think the preferred way would be to do the calculation in SQL
SELECT Cost_Goods -Sales_Price AS profit FROM someTable ORDER BY profit
Or if you can create a view in the database...
CREATE VIEW sales_profit AS SELECT ID, Cost_Goods, Sales_Price, Cost_Goods -Sales_Price AS profit FROM someTable;
Then you can..
SELECT * FROM sales_profit where ID = 2;
SELECT * FROM sales_profit ORDER BY profit;

Related

Can MySQL/MariaDB stop doing a selection by reaching certain condition? Or select all rows before condition

For example, I have table
id;name
1;John
2;Mary
3;Cat
4;Cheng
I want selection to stop right after 3;Cat and still have as much rows in it as exist berore 3;Cat
I think this could be described with such a query
SELECT * FROM table WHERE condition ORDER BY id LIMIT name = 'Cat'
but of course there is no such a construction LIMIT name='Cat' in SQL.
Maybe something else fits?
Currently Im using extensive select, but it requires enormous 1200 rows to be sure that it has at least one record expected.
This is a not-so-ad answer
https://stackoverflow.com/a/22232897/1475428
Solution might look like
SELECT * WHERE id <= (SELECT MIN(id) WHERE name = 'Cat') order by id
MIN function plays role of backward approach that works like conditional LIMIT.
This looks like an ugly way, I still think there might be a better solution.
This is quite awkward to do in a single query. That means you probably should not try to do it in a single query.
Sometimes it's simpler to do a complex task in several steps. It's easier to write, it's easier to debug, it's easier to modify if you need to, and it's easier for future programmers to read your code if they need to take over responsibility.
So first query for the condition, and find out the id of the row you want to stop at:
SELECT MIN(id) FROM mytable WHERE name = 'Cat';
This returns either an id value, or else NULL if there is no row matching the condition.
If that result was not NULL, then use that value to run a simple query:
SELECT * FROM mytable WHERE id <= ? ORDER BY id
Else if the result was NULL, then default to a query with the fixed LIMIT you want:
SELECT * FROM mytable ORDER BY id LIMIT ?
If you have special conditions that aren't supported by simple SQL, then break it up into different queries that are each simple, and use a little bit of application logic to choose which query to run.

How to return only 10 rows via SQL query

I have 1 query that returns over 180k rows. I need to make a slight change, so that it returns only about 10 less.
How do I show only the 10 rows as a result?
I've tried EXCEPT but it seems to return a lot more than just the 10.
You can use LIMIT. This will show first n rows. Example:
SELECT * FROM Orders LIMIT 10
If you are trying to make pagination add OFFSET. It will return 10 rows starting from row 20. Example:
SELECT * FROM Orders LIMIT 10 OFFSET 20
MySQL doesn't support EXCEPT (to my knowledge).
Probably the most efficient route would be to incorporate the two WHERE clauses into one. I say efficient in the sense of "Do it that way if you're going to run this query in a regular report or production application."
For example:
-- Query 1
SELECT * FROM table WHERE `someDate`>'2016-01-01'
-- Query 2
SELECT * FROM table WHERE `someDate`>'2016-01-10'
-- Becomes
SELECT * FROM table WHERE `someDate` BETWEEN '2016-01-01' AND '2016-01-10'
It's possible you're implying that the queries are quite complicated, and you're after a quick (read: not necessarily efficient) way of getting the difference for a one-off investigation.
That being the case, you could abuse UNION and a sub-query:
(Untested, treat as pseudo-SQL...)
SELECT
*
FROM (
SELECT * FROM table WHERE `someDate`>'2016-01-01'
UNION ALL
SELECT * FROM table WHERE `someDate`>'2016-01-10'
) AS sub
GROUP BY
`primaryKey`
HAVING
COUNT(1) = 1;
It's ugly though. And not efficient.
Assuming that the only difference is only that one side (I'll call it the "right hand side") is missing records that the left includes, you could LEFT JOIN the two queries (as subs) and filter to right-side-is-null. But that'd be dependent on all those caveats being true.
Temporary tables can be your friend - especially given they're so easily created (and can be indexed):
CREATE TEMPORARY TABLE tmp_xyz AS SELECT ... FROM ... WHERE ...;

Simulate MySQL records using inline data

This may sound like an odd question, but I'm curious to know if it's possible...
Is there a way to simulate MySQL records using inline data? For instance, if it is possible, I would expect it to work something like this:
SELECT inlinedata.*
FROM (
('Emily' AS name, 26 AS age),
('Paul' AS name, 56 AS age)
) AS inlinedata
ORDER BY age
Unfortunately MySQL does not support the standard values row-constructor for this kind of things, so you need to use a "dummy" select for each row and combine the rows using UNION ALL
SELECT *
FROM (
select 'Emily' AS name, 26 AS age
union all
select 'Paul', 56
) AS inlinedata
ORDER BY age
The UNION ALL serves two purposes
It preserves any duplicate you might have on purpose
It's a (tiny) bit faster than a plain UNION (because it does not check for duplicates)
No, not without making it complicated, but you can create a temporary table and query that instead. Temporary tables are deleted when the current client session terminates.
You can query them and insert data into them just like with other tables. When you create them, you have to use the TEMPORARY keyword, like so:
CREATE TEMPORARY TABLE ...
This way, you can also reuse the data for multiple queries if needed, no data gets stored, and all records that you query have the right structure (whereas the syntax you give in your example would create problems when you spell a column name wrong)...
with cte as (
select '2012-04-04' as student_dob, '%test1%' as student_pat
union all
select '2012-05-04', '%test2%'
union all
select '2012-07-04', '%test3%'
union all
select '2012-05-11', '%test-n%'
)
select *
from students s
inner join cte c
on s.student_dob = c.student_dob and s.student_name like c.student_pat
arguably that's not a lot more readable, but taking a lead from that, you can just store those in a table or go through temporary table, like Roy suggested.
Also it's not great idea to make a group by student id and select also something else like you did in 2nd query.

SQL queries to determine all values that would satisfy an arbitrary query

I'm trying to figure out how to efficiently run a set of queries that will provide a new table of all values that would return results for an arbitrary query.
Say my table has a schema like:
id
name
age
city
What is an efficient way to list all values that would return results for an arbitrary query (e.g., SELECT * FROM main WHERE NOT city=X AND age BETWEEN Y AND Z)?
My naive approach for this would be to use a script and recurse through all possible combinations of {city, age, age} and see which SELECTs return more than 0 results, but that seems incredibly inefficient. I've also tried building large joins on {city, age, age} as well and basically using that table as an argument list to the query, but that quickly becomes an impossibility for queries on many columns.
For simple conjunctive equality queries (e.g., SELECT * FROM main WHERE name=X AND age=Y), this is much simpler, as I can do something like:
SELECT name, age, count(*) AS count FROM main GROUP BY name, age HAVING count > 0
But I'm having difficulty coming up with a general approach for anything more complicated than that.
Any pointers in the right direction would be most helpful, thanks.
EDIT:
It appears I did a very poor job explaining this, sorry.
Imagine a user gives me a database and a template query and says, "Tell me all the values I can use in this query that will yield results from this database." For example, the user might want to know all age range queries that will return at least one row (e.g., the template query is SELECT * FROM main WHERE age BETWEEN X AND Y).
In that particular example, one could run a SELECT to find the min/max ages in the database, and just tell the user to query between those ages.
Now imagine that the query template is more complicated, such as SELECT * FROM main WHERE NOT city=W AND age BETWEEN X AND Y AND name LIKE Z. How could one determine the range of W/X/Y/Z values that can be used with this query to return results? Does it require creating a join table with every single {city, age, age, name} combination and running the SELECT on each row? How can I do this efficiently so that the operation is time-bound on large databases?
Hopefully that clarifies it.
You could try to write a trigger after insert into your table which inserts the values into another table if they don't exist already. This way you'd have a table with the distinct values of your table. This table would look something like
columnNameFromYourTable | distinctValue
city NY
city LA
age 1
age 2
...
Then when you want to know if a record exists in your table for query SELECT * FROM main WHERE NOT city=W AND age BETWEEN X AND Y AND name LIKE Z you'd query the distinctTable with
select 1 from dual where 1=1
and not exists (select 1 from distinctTable where columnNameFromYourTable = 'city' and distinctValue = 'W')
and exists (select 1 from distinctTable where columnNameFromYourTable = 'age' and distinctValue BETWEEN X AND Y)
and exists (select 1 from distinctTable where columnNameFromYourTable = 'name' and distinctValue LIKE '%Z%')
This would be pretty fast then. If it returns 1 there is an entry in your table, if NULL not.

What is the most efficient MySQL query to find all entries that start with a number?

In a database that has over 1 million entries, occasionally we need to find all rows that have a column name that starts with a number.
This is what currently is being used, but it just seems like there may be a more efficient manner in doing this.
SELECT * FROM mytable WHERE name LIKE '0%' OR name LIKE '1%' OR name ...
etc...
Any suggestions?
select * from table where your_field regexp '^[0-9]'
Hey,
you should add an index with a length of 1 to the field in the db. The query will then be significantly faster.
ALTER TABLE `database`.`table` ADD INDEX `indexName` ( `column` ( 1 ) )
Felix
My guess is that the indexes on the table aren't being used efficiently (if at all)
Since this is a char field of some type, and if this is the primary query on this table, you could restructure your indexes (and my mysql knowledge is a bit short here, somebody help out) such that this table is ordered (clustered index in ms sql) by this field, thus you could say something like
select * from mytable where name < char(57) and name > char(47)
Do some testing there, I'm not 100% on the details of how mysql would rank those characters, but that should get you going.
Another option is to have a new column that gives you a true/false on "starts_with_number". You could setup a trigger to populate that column. This might give the best and most predictable results.
If you're not actually using each and every field in the rows returned, and you really want to wring every drop of efficiency out of this query, then don't use select *, but instead specify only the fields you want to process.
I'm thinking...
SELECT * FROM myTable WHERE IF( LEFT( name, 1) = '#', 1,0)