Use a generated column in MariaDB 5.5.52 - mysql

I have a valid sql statement that looks like this:
SELECT SUM(CASE WHEN faell_art='monatlich' THEN betrag*12 END) + SUM(CASE WHEN faell_art='vierteljährlich' THEN betrag*4 END) + SUM(CASE WHEN faell_art='halbjährlich' THEN betrag*2 END) + SUM(CASE WHEN faell_art='jährlich' THEN betrag END) as total FROM banking
Now I want to create a generated column which does exactly the same. I tried this statement:
ALTER TABLE `banking` ADD `test` DECIMAL(10,2) AS (SUM(CASE WHEN faell_art='monatlich' THEN betrag*12 END) + SUM(CASE WHEN faell_art='vierteljährlich' THEN betrag*4 END) + SUM(CASE WHEN faell_art='halbjährlich' THEN betrag*2 END) + SUM(CASE WHEN faell_art='jährlich' THEN betrag END)) AFTER `kommentar`;
However it doesn´t work ("#1901 - Function or expression is not allowed for column 'test'").
Any help? Thx :-)

There is a limitation:
subqueries or anything that depends on data outside the row are not
allowed (these are not deterministic because the underlying data can
change).
All aggregate functions (SUM,...) operate with other records in the table.
Virtual (Computed) Columns.
So, that generated column cannot be created. Or, you could create a view.

Related

I want to show total work hours with minutes in SQL query

I want to show total work hours with minutes in SQL query
select
mom.machinery_operation_master_id,
mom.assigned_code,
mom.record_type,
(case when mom.record_type=1 then time(mom.entered_time) end) power_on_time,
(case when mom.record_type=2 then time(mom.entered_time) end) power_off_time,
(case when mom.record_type=3 then time(mom.entered_time) end) breakdown_time,
(case when mom.record_type=4 then time(mom.entered_time) end) repaired_time
from machinery_operation_master mom
where mom.location_id = '9' and
mom.created_at between '2019-09-17 08:00:00' and '2019-09-18 07:59:00'
suppose power on time : 13:00:00 in 1st row and in second row power off time : 14:30:00, then that my expected output should be like this. total hours with minutes : 1:30:00
using mariadb: result is HH:mm
select concat((TIMESTAMPDIFF(second, t1.power_on_time, t1.power_off_time)
- TIMESTAMPDIFF(second, t1.breakdown_time, t1.repaired_time))/3600, ':',
((TIMESTAMPDIFF(MINUTE, t1.power_on_time, t1.power_off_time)
- TIMESTAMPDIFF(MINUTE, t1.breakdown_time, t1.repaired_time)) % 60), ':00') as working_time
from
(select
mom.machinery_operation_master_id,
mom.assigned_code,
mom.record_type,
(case when mom.record_type=1 then time(mom.entered_time) end) power_on_time,
(case when mom.record_type=2 then time(mom.entered_time) end) power_off_time,
(case when mom.record_type=3 then time(mom.entered_time) end) breakdown_time,
(case when mom.record_type=4 then time(mom.entered_time) end) repaired_time,
from machinery_operation_master mom
where mom.location_id = '9' and
mom.created_at between '2019-09-17 08:00:00' and '2019-09-18 07:59:00') as t1
In MySQL, I think it would look something like this...
TIMEDIFF(power_on_time, power_off_time)
Which should give you a time value (hours and minutes) already.
More details: https://dev.mysql.com/doc/refman/8.0/en/date-and-time-functions.html#function_timediff
Assuming MS SQL Server, and that all of your columns are DATETIME columns...
I guess you'd start with getting a DATEDIFF(hh, power_on_time, power_off_time) to get the number of hours between them; DATEDIFF(mi, DATEADD(hh, DATEDIFF(hh, power_on_time, power_off_time), power_on_time), power_off_time) will get the number of minutes (remainder).
These can be combined and cast to a datetime if you need that.
Then use the same method to calculate 'broken down time' and subtract it from your 'powered on time' - I'm broadly guessing at the details based on your code sample.

How to group same kind of values into common one and group by that

Hi I was looking for a mysql query result like
As you can see there are some values have the kind of values (Ex: BV and BR or C5 and C7) how can I combine then together into one common value lets say B or C and group by that in sql?
I have the following query:
SELECT
type,
sum(case when status ='valid' then 1 else 0 end) valid_jobs,
sum(case when status ='non-valid' then 1 else 0 end) non_valid_jobs,
sum(case when status IS NULL then 1 else 0 end) null_jobs
from
main_table
where
SUBSTRING_INDEX(CAST(CAST(from_unixtime(date_generated) AS DATE) AS CHAR), '-',2) REGEXP '^2016'
group by type
Thanks in advance guys.
Otcome will look like:
Just use an expression that evaluates the value of the type column, and returns the desired result.
What's not clear from the question is the "mapping" from type to the value you want returned in the first column. It looks like we might be looking at just the first character of value in the type column.
SUBSTR(type,1,1)
If the "mapping" is more involved, then we could use a CASE expression. For example:
CASE
WHEN type IN ('BV','BR','BT','ZB') THEN 'B'
WHEN type IN ('C5','C7') THEN 'C'
WHEN ... THEN ...
ELSE type
END
We'd use that as the first expression in the SELECT list (replacing the reference to the type column in the original query), and in the GROUP BY clause.
On an (unrelated) performance note, we'd prefer conditions in the WHERE clause to be on bare columns. That allows MySQL to make use of an (efficient) range scan operation on an appropriate index.
With this condition:
WHERE SUBSTRING_INDEX(CAST(CAST(FROM_UNIXTIME( t.date_generated ) AS DATE) AS CHAR), '-',2)
REGEXP '^2016'
We're forcing MySQL to evaluate the expression on the left side for every row in the table. And the value returned by the expression is compared.
If what we're really trying to do is get date_generated values in 2016, assuming that date_generated is INTEGER type, storing 32-bit unix-style number of seconds since beginning of the era 1970-01-01...
We can do something like this:
WHERE t.date_generated >= UNIX_TIMESTAMP('2016-01-01')
AND t.date_generated < UNIX_TIMESTAMP('2017-01-01')
MySQL will see that as a range operation on the values in te date_generated column. And with that, MySQL can make effective use of an index that has date_generated as a leading column.
Just replace expr with the expression that returns the values you want in the first column:
SELECT expr
, SUM(IF( t.status = 'valid' ,1,0)) AS valid_jobs
, SUM(IF( t.status = 'non-valid' ,1,0)) AS non_valid_jobs
, SUM(IF( t.status IS NULL ,1,0)) AS null_jobs
FROM main_table t
WHERE t.date_generated >= UNIX_TIMESTAMP('2016-01-01')
AND t.date_generated < UNIX_TIMESTAMP('2017-01-01')
GROUP BY expr
EDIT
To guarantee that rows are returned in a particular sequence, add an ORDER BY clause, e.g.
ORDER BY 1
try this,
SELECT
LEFT(type,1) AS type,
sum(case when status ='valid' then 1 else 0 end) valid_jobs,
sum(case when status ='non-valid' then 1 else 0 end) non_valid_jobs,
sum(case when status IS NULL then 1 else 0 end) null_jobs
FROM
main_table
WHERE
SUBSTRING_INDEX(CAST(CAST(from_unixtime(date_generated) AS DATE) AS CHAR), '-',2) REGEXP '^2016'
GROUP BY
type

sums return String, only with postgresql

I am migrating a database from mysql to postgres. The migration itself was ok, following the postgres documentation.
Right now, I'm fixing our specific mysql queries.
In some point, we have now something like this:
select(%(
SUM(CASE WHEN income THEN value ELSE 0 END) AS rents,
SUM(CASE WHEN not income THEN value ELSE 0 END) AS expenses
))
In mysql, it was a sum(if(incomes, value, 0)) etc, and it was working as expected.
With PG, it returns a string instead of a numeric.
I already checked the database and the data type is correct.
What can I do, besides cast to_d or to_f?
EDIT: the complete query:
SELECT
SUM(CASE WHEN income THEN value ELSE 0 END) AS rents,
SUM(CASE WHEN not income THEN value ELSE 0 END) AS expenses
FROM "transactions"
WHERE "transactions"."type" IN ('Transaction')
AND "transactions"."user_id" = 1
AND "transactions"."paid" = 't'
AND (transactions.date between '2013-09-01' and '2013-09-30')
LIMIT 1
As far as I know, using .to_f, .to_i or whatever is the answer - the Rails PostGres adapter seems adamant that everything is a String unless it's an ActiveRecord model.
See: connection.select_value only returns strings in postgres with pg gem
I don't particularly approve of this, but it is, as the saying goes, 'working as intended'.

Using Sum inside a Case statement in MySQL

I am having problem in MySQL adding three values, this should be simple right?
I have code that selects values from a column based upon the value of a second column, I use a case statement like this:
Select
Max(Case
When Table1.costcode Like '%Costcode1%'
Then Table1.costs
Else Null End) As 'Costcode1',
Max(Case
When Table1.costcode Like '%Costcode2%'
Then Table1.costs
Else Null End) As 'Costcode2',
Max(Case
When Table1.costcode Like '%Costcode3%'
Then Table1.costs
Else Null End) As 'Costcode3',
(Case
When Table1.costcode In ('%Costcode1%','%Costcode2%','%Costcode3%')
Then Sum(Table1.costs)
Else Null End) As 'Total Cost',
From Table1
the first three Case statements work fine and all return values (these are held in the database as negative numbers e.g. -13624.00), however the Total Cost Case just returns Null...
The column Table1.costcode includes many other codes as well so I can't just sum all of the values without picking them out first.
It must be simple to sum these values, but obviously I'm missing something… Help, please :-)
Thanks
Try this -
SUM(Case Table1.costcode
When LIKE ('%Costcode1%') Then Table1.costs
When LIKE ('%Costcode2%') Then Table1.costs
When LIKE ('%Costcode3%') Then Table1.costs
Then Table1.costs
Else 0.00 End) As 'Total Cost'

How do I add a column with an auto-incremental name to a MySql table?

I'm using VB.net to write a MYSQL application and one feature I need to add is the ability to add a column for each week that my employees worked. For example I want the first column to be called "W1" and the next one to be "W2" and so on, right up to "W52". so my question is how would I add a column to my table and have it's name add 1 to its current value? if there wasn't any letters in the name it would be easy but I need it to have the "W" in the title as well to avoid confusion. to add the column I have this:
ALTER TABLE manager ADD W1 MEDIUMINT;
i just need the part that adds an INTEGER to a VARCHAR datatype if possible... Maybe there should be some sort of data type conversion involved?
UPDATE:
what i want to display in my vb.net form is a datagrid view that looks exactly like this:
Just to explain further, the black bar at the very top are the date stamps for each week, I would like to have this included in the datagridview if possible but it is not required. Under each week column the employees will be entering the percents they worked (example: 20%, or 0.20 is one day of work) I know this is an odd way of doing things but it's mandatory... and I was wondering if it were possible to automate the creation of these columns rather than me having to manually enter them.
1 possible solution would be to create 2 new tables
tbl_week:
weekid, weekno, start_date, end_date
tbl_weeks_worked:
userid, weekid, worked
and pivot the data in the database. (alternativly you could do this in VB.NET)
if you want the header, you will need to union 2 pivoted queries
e.g:
SELECT '' as userid,
MAX(CASE WHEN w.weekno = 1 THEN w.start_date END)) AS 'W1',
MAX(CASE WHEN w.weekno = 2 THEN w.start_date END)) AS 'W2',
MAX(CASE WHEN w.weekno = 3 THEN w.start_date END)) AS 'W3',
........ etc
FROM tbl_week w
WHERE w.start_date >= start AND w.end_date <= enddate
UNION ALL
SELECT
userid,
SUM(CASE WHEN w.weekno = 1 AND ww.weekid IS NOT NULL THEN worked ELSE 0 END)) AS 'W1',
SUM(CASE WHEN w.weekno = 2 AND ww.weekid IS NOT NULL THEN worked ELSE 0 END)) AS 'W2',
SUM(CASE WHEN w.weekno = 3 AND ww.weekid IS NOT NULL THEN worked ELSE 0 END)) AS 'W3',
........ etc
FROM tbl_week w
LEFT JOIN tbl_weeks_worked ww ON ww.weekid = w.weekid
WHERE w.start_date >= start AND w.end_date <= enddate
GROUP BY userid;
Note: (this will only work for a single year view)