SUM(IF(COND,EXPR,NULL)) and IF(COND, SUM(EXPR),NULL) - mysql

I'm working of generating sql request by parsing Excel-like formulas.
So for a given formula, I get this request :
SELECT IF(COL1='Y', SUM(EXPR),NULL)
FROM Table
I don't get the results I want. If I manually rewrite the request like this it works :
SELECT SUM(IF(COL1='Y', EXPR, NULL))
FROM Table
Also, the first request produces the right value if I add a GROUP BY statement, for COL1='Y' row :
SELECT IF(COL1='Y', SUM(EXPR),NULL)
FROM Table
GROUP BY COL1
Is there a way to keep the first syntax IF(COND, SUM(EXPR), NULL) and slightly edit it to make it works without a GROUP BY statement ?

You have to use GROUP BY since you are using SUM - otherwise SQL engine is not able to tell how do you want to summarize the column.
Alternatively you could summarize this column only:
SELECT SUM(EXPR)
FROM Table
WHERE COL1='Y'
But then you would have to run separate query for each such column, read: not recommended for performance reasons.

Related

Can SqlAlchemy's array_agg function accept more than one column?

I want to return arrays with data from the entire row (so all columns), not just a single column. I can do this with a raw sql statement in Postgresql,
SELECT
array_agg(users.*)
FROM users
WHERE
l_name LIKE 'Br%'
GROUP BY f_name;
but when I try to do it with SqlAlchemy, I'm getting
sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) can't adapt type 'InstrumentedAttribute'
For example, when I execute this query, it works fine
query: Query[User] = session.query(array_agg(self.user.f_name))
But with this I get arrays of rows with only one column value in them (in this example, the first name of a user) whereas I want the entire row (all columns for a user).
I've tried explicitly listing multiple columns, but to no avail. For example I've tried this:
query: Query[User] = session.query(array_agg((self.user.f_name, self.user.l_name))))
But it doesn't work. I get the above error message.
You could use Python feature unpack for create
example = [func.array_agg(column) for column in self.example.__table__.columns]
query = self.dbsession.query(*attach)
And after join results

how to include hard-coded value to output from mysql query?

I've created a MySQL sproc which returns 3 separate result sets. I'm implementing the npm mysql package downstream to exec the sproc and get a result structured in json with the 3 result sets. I need the ability to filter the json result sets that are returned based on some type of indicator in each result set. For example, if I wanted to get the result set from the json response which deals specifically with Suppliers then I could use some type of js filter similar to this:
var supplierResultSet = mySqlJsonResults.filter(x => x.ResultType === 'SupplierResults');
I think SQL Server provides the ability to include a hard-coded column value in a SQL result set like this:
select
'SupplierResults',
*
from
supplier
However, this approach appears to be invalid in MySQL b/c MySQL Workbench is telling me that the sproc syntax is invalid and won't let me save the changes. Do you know if something like what I'm trying to achieve is possible in MySQL and if not then can you recommend alternative approaches that would help me achieve my ultimate goal of including some type of fixed indicator in each result set to provide a handle for downstream filtering of the json response?
If I followed you correctly, you just need to prefix * with the table name or alias:
select 'SupplierResults' hardcoded, s.* from supplier s
As far as I know, this is the SQL Standard. select * is valid only when no other expression is added in the selec clause; SQL Server is lax about this, but most other databases follow the standard.
It is also a good idea to assign a name to the column that contains the hardcoded value (I named it hardcoded in the above query).
In MySQL you can simply put the * first:
SELECT *, 'SupplierResults'
FROM supplier
Demo on dbfiddle
To be more specific, in your case, in your query you would need to do this
select
'SupplierResults',
supplier.* -- <-- this
from
supplier
Try this
create table a (f1 int);
insert into a values (1);
select 'xxx', f1, a.* from a;
Basically, if there are other fields in select, prefix '*' with table name or alias

Statement in trigger is not "picking up" the condition in its Where clause

so I'm currently working on a MySQL trigger. I'm trying to assign values to two variables when a new record is inserted. Below are the queries:
SET mssgDocNo = (SELECT Document_ID FROM CORE_MSSG WHERE Message_ID = new.MSSG_ID);
SET mssgRegime = (SELECT CONCAT (Regime_Type, Regime_Code) FROM T_DOC WHERE CD_Message_ID = new.MSSG_ID);;
For some reason, the second SQL query is not picking up the 'new.MSSG_ID' condition while the first query in same trigger recognizes it. I really can't figure out what seems to be the problem.
When I replace the 'new.MSSG_ID' with a hard-coded value from the database in the second query it seems to work. I doubt the 'new.MSSG_ID' is the problem because it works perfectly fine in the first query.
I've tried pretty much anything I could think of. Would appreciate the help.
I would write these more simply as:
SELECT mssgDocNo := Document_ID
FROM CORE_MSSG
WHERE Message_ID = new.MSSG_ID;
SELECT mssgRegime := CONCAT(Regime_Type, Regime_Code)
FROM T_DOC
WHERE CD_Message_ID = new.MSSG_ID;
The SET is not necessary.
I did make one other change that might make it work. I removed the space after CONCAT. Some versions of MySQL have trouble parsing spaces after function calls.

Not the same result between SqLite and MariaDb SELECT

I have 2 tables on Debian server, rides and steps.
From a Xamarin app I get data from this server (Refit, Newtonsoft Json and SQLite-net PCL packages) to populate local tables.
When I use this query on mariadb:
SELECT 1_steps.*
FROM 1_rides, 1_steps
WHERE 1_rides.id=1_steps.ride_id
AND 1_rides.start=1
GROUP BY 1_rides.id
I got correct results (first step of each ride, then it starts with 1)
But when use equivalent for SqLite:
SELECT Steps.*
FROM Rides,Steps
WHERE Rides.Id=Steps.RideId
AND Rides.Start=1
GROUP BY Rides.Id
In the result, I get the last step of each (same) ride!
Whether on mariadb or sqlite, each table has a primary key (id field).
I checked, the data is sent, received and saved in the same order.
Simply added in mobile app with:
foreach (var step in await App.RestClient.getSteps())
if (dbCon.InsertOrReplace(step) != 1)
....
I tried adding ORDER BY Rides.Id but that does not change anything.
You are relying on something that is not allowed by strict SQL standards: whenever you have a group by clause, the fields in the select clause must either appear in the group by clause as well, or must be aggregations (e.g. min, count), or must be functionally dependent on the group by fields.
In your case those conditions are not met and so if the DB engine allows this, it will have to decide which value to pick within a same group: the first, the last, or still something else.
The way to deal with this, is to be explicit what you want to get in such a case, by specifying an aggregation:
SELECT 1_steps.id,
min(1_steps.step),
max(1_steps.whatever),
avg(1_steps.some_number),
FROM 1_rides
INNER JOIN 1_steps
ON 1_rides.id=1_steps.ride_id
WHERE 1_rides.start=1
GROUP BY 1_rides.id
You did not specify the fields of your table, but the idea should be clear: list the fields separately (not *), and apply the type of aggregation to them you need.
Alternative
If you are not interested in aggregating anything, but just want one particular record from steps per ride, then don't use group by, but specify the condition that filters exactly that one record from steps:
SELECT 1_steps.*
FROM 1_rides
INNER JOIN 1_steps
ON 1_rides.id=1_steps.ride_id
WHERE 1_rides.start=1
AND 1_steps.step = 1
ORDER BY 1_rides.id
Note the condition 1_steps.step = 1: you'll have to decide what that condition should be of course.

MySQL using table columns in function to create alias to be used in sorting

It sounds more complicated than it actually is. Here is what I'm trying to do within the SELECT part:
SELECT TIMESTAMPADD(
UCASE(
SUBSTRING(offset_unit,1,CHAR_LENGTH(offset_unit)-1)
),1,'2003-01-02') as offset_date
offset_unit is a VARCHAR column in the database. It contains one of the following: "Hours","Minutes".
offset is an INT.
I am trying to convert the offset_unit to uppercase, after I have removed the last character ('s') so I can have a proper interval (MINUTE, HOUR...) so I can get a date that I can use in sorting afterwards, but MySQL keeps throwing an error. I have tested each step by adding one function at a time, and it only fails after I add TIMESTAMPADD. If I enter MINUTE manually then it works.
Any way to get this working?
Additional info: I am running this in CakePHP 1.3, in a find, within the 'fields' array, but that shouldn't be important.
this can be easily achived by using CASE WHEN clause as:
SELECT (CASE
WHEN offset_unit = 'HOURS'
THEN TIMESTAMPADD(HOUR,`offset`,'2003-01-02')
WHEN offset_unit = 'MINUTES'
THEN TIMESTAMPADD(MINUTE,`offset`,'2003-01-02')
END) AS offset_date
FROM my_table;
SEE SQLFIDDLE DEMO HERE
It doesn't work because TIMESTAMPADD does not take a string as the first argument, but a unit keyword, for example MINUTE. My guess is that you need to do this in two steps, first get the unit and then construct a query with the correct keyword.