MariaDB server version for the right syntax to use near 'GROUP BY - sql

I build my project based on Laravel 9 and try to get count data by date group. I write using DB::raw to get sql query like this:
$rawActive = "
SELECT
SBC.SITE,
OPR.OPERATOR,
COUNT(*) TMO_COUNT,
DATE_FORMAT( TMO.TMO_DATE, '%m%Y' ) BULANTAHUN
FROM
TOP_TMO TMO
INNER JOIN SUBSCRIBER SBC ON TMO.SUBSCRIBER_ID = SBC.ID
INNER JOIN OPERATOR OPR ON SBC.SITE_ID = OPR.ID
WHERE
SBC.SITE_ID = ".$siteId."
GROUP BY
DATE_FORMAT(
TMO.TMO_DATE,
'%m%Y')
";
$queryAct = DB::select(DB::raw($rawActive));
the siteId is from form request.
I search for some solutions include edit 'strict' => false, in database.php , but still not find any solution.
I try to return $rawActive, and this is the result.
SELECT
SBC.SITE,
OPR.OPERATOR,
COUNT(*) TMO_COUNT,
DATE_FORMAT( TMO.TMO_DATE, '%m%Y' ) BULANTAHUN
FROM
TOP_TMO TMO
INNER JOIN SUBSCRIBER SBC ON TMO.SUBSCRIBER_ID = SBC.ID
INNER JOIN OPERATOR OPR ON SBC.SITE_ID = OPR.ID
WHERE
SBC.SITE_ID = 134
GROUP BY
DATE_FORMAT(
TMO.TMO_DATE,
'%m%Y')
As you can see, the siteId are seen well.
I also try this query on mysql, it's work fine.
Thanks for your help.

You need to adjust config\database.php as below:
'mysql' => [
...
....
'strict' => true,
'modes' => [
//'ONLY_FULL_GROUP_BY', // Disable this to allow grouping by one column
'STRICT_TRANS_TABLES',
'NO_ZERO_IN_DATE',
'NO_ZERO_DATE',
'ERROR_FOR_DIVISION_BY_ZERO',
'NO_AUTO_CREATE_USER',
'NO_ENGINE_SUBSTITUTION'
],
]

You can try this. You can enclose $siteId with single quote. '123' will work same like 123 and it will helps breaking query when there is no value assigned in $siteId. Instead try to use parameterized query that will prevent this issue and also is recommended solution for securing raw query.
$rawActive = "
SELECT
SBC.SITE,
OPR.OPERATOR,
COUNT(*) TMO_COUNT,
DATE_FORMAT( TMO.TMO_DATE, '%m%Y' ) BULANTAHUN
FROM
TOP_TMO TMO
INNER JOIN SUBSCRIBER SBC ON TMO.SUBSCRIBER_ID = SBC.ID
INNER JOIN OPERATOR OPR ON SBC.SITE_ID = OPR.ID
WHERE
SBC.SITE_ID = '".$siteId."'
GROUP BY
DATE_FORMAT(
TMO.TMO_DATE,
'%m%Y')
";
$queryAct = DB::select(DB::raw($rawActive));

Related

Postgresql combine multiple rows into multiple columns

I have following sql:
with modules as (
select *
from translate_dev.tab_module as m
join translate_dev.tab_translation_module as tm on m.id = tm.module_id
)
select tm.name,
t.id,
t.language_id,
t.text
from translate_dev.tab_translation as t
join modules as tm on tm.translation_id = t.id
where t.id in (
1166615, 1166401, 1166578, 1166579, 1166580, 1166581, 1166582, 1166583, 1166584, 1166586, 1166587,
1166588, 1166589, 1166591, 1166595, 1166597, 1166598, 1166599, 1166600, 1166601, 1166602, 1166603,
1166604, 1166605, 1166606, 1166607, 1166608, 1166610, 1166612, 1166614, 1166616, 1166617, 1166618,
1166619, 1166621, 1166623, 1166624, 1166626, 1166627, 1166628, 1166629, 1166631, 1166632, 1166633,
1166634, 1166635, 1166636, 1166637, 1166638, 1166640, 1166641, 1166642, 1166643, 1166644, 1166645,
1166646, 1166650, 1166651, 1166652, 1166653, 1166654, 1166655, 1166656, 1166657, 1166658, 1166659,
1166662, 1166664, 1166665, 1166667, 1166668, 1166669, 1166671, 1166672, 1166673, 1166674, 1166675,
1166676, 1166677, 1166678, 1166679, 1166680, 1166681, 1166682, 1166683, 1166685, 1166688, 1166689,
1166693, 1166696, 1166697, 1166698, 1166699, 1166700, 1166701, 1166702, 1166704, 1166705, 1166706,
1166707, 1166709, 1166710, 1166712, 1166713, 1166714, 1166716, 1166717, 1166718, 1166719, 1166721,
1166722, 1166723, 1166725, 1166726, 1166727, 1166728, 1166730, 1166731, 1166733, 1166734, 1166735,
1166736, 1166741, 1166742, 1166743, 1166744, 1166745, 1166747, 1166748, 1166749, 1166751, 1166752,
1166753, 1166754, 1166755, 1166756, 1166757, 1166758, 1166759, 1166760, 1167155, 1167157, 1167158,
1167539, 1167540, 1167546, 1167966, 1167967, 1168007, 1168010, 1168011, 1168012, 1168014, 1168015,
1168016, 1168017, 1168018, 1168019, 1168020, 1168021, 1168022, 1168023, 1168024, 1168025, 1168026,
1168027, 1168028, 1168029, 1168030, 1168031, 1168032, 1168033, 1168034, 1168035
)
order by t.id, t.language_id
Which results in (only an example):
What i want though is one result row for the id in which the text of the specific language_id is appended as extra column.
What i mean is:
Name
id
bg
cs
de
...
MobileServices
1166401`
bg translated text
cs translated text
de translated text
...
Someone has an idea how to do this? I tried it the crosstab function but since the sql needs to be a string in the function i have problems figuring out the error stacktraces.
Any help is appreciated!
Thanks
You can use conditional aggregation. Postgres provides filter for this purpose:
select tm.name, t.id,
max(t.text) filter (where t.language_id = 'bg') as bg,
max(t.text) filter (where t.language_id = 'cs') as cs,
. . .
from translate_dev.tab_translation t join
modules tm
on tm.translation_id = t.id
where t.id in (. . . )
group by tm.name, t.id

How to dynamic, handle nested WHERE AND/OR queries using Rails and SQL

I'm currently building a feature that requires me to loop over an hash, and for each key in the hash, dynamically modify an SQL query.
The actual SQL query should look something like this:
select * from space_dates d
inner join space_prices p on p.space_date_id = d.id
where d.space_id = ?
and d.date between ? and ?
and (
(p.price_type = 'monthly' and p.price_cents <> 9360) or
(p.price_type = 'daily' and p.price_cents <> 66198) or
(p.price_type = 'hourly' and p.price_cents <> 66198) # This part should be added in dynamically
)
The last and query is to be added dynamically, as you can see, I basically need only one of the conditions to be true but not all.
query = space.dates
.joins(:price)
.where('date between ? and ?', start_date, end_date)
# We are looping over the rails enum (hash) and getting the key for each key value pair, alongside the index
SpacePrice.price_types.each_with_index do |(price_type, _), index|
amount_cents = space.send("#{price_type}_price").price_cents
query = if index.positive? # It's not the first item so we want to chain it as an 'OR'
query.or(
space.dates
.joins(:price)
.where('space_prices.price_type = ?', price_type)
.where('space_prices.price_cents <> ?', amount_cents)
)
else
query # It's the first item, chain it as an and
.where('space_prices.price_type = ?', price_type)
.where('space_prices.price_cents <> ?', amount_cents)
end
end
The output of this in rails is:
SELECT "space_dates".* FROM "space_dates"
INNER JOIN "space_prices" ON "space_prices"."space_date_id" = "space_dates"."id"
WHERE "space_dates"."space_id" = $1 AND (
(
(date between '2020-06-11' and '2020-06-11') AND
(space_prices.price_type = 'hourly') AND (space_prices.price_cents <> 9360) OR
(space_prices.price_type = 'daily') AND (space_prices.price_cents <> 66198)) OR
(space_prices.price_type = 'monthly') AND (space_prices.price_cents <> 5500)
) LIMIT $2
Which isn't as expected. I need to wrap the last few lines in another set of round brackets in order to produce the same output. I'm not sure how to go about this using ActiveRecord.
It's not possible for me to use find_by_sql since this would be dynamically generated SQL too.
So, I managed to solve this in about an hour using Arel with rails
dt = SpaceDate.arel_table
pt = SpacePrice.arel_table
combined_clauses = SpacePrice.price_types.map do |price_type, _|
amount_cents = space.send("#{price_type}_price").price_cents
pt[:price_type]
.eq(price_type)
.and(pt[:price_cents].not_eq(amount_cents))
end.reduce(&:or)
space.dates
.joins(:price)
.where(dt[:date].between(start_date..end_date).and(combined_clauses))
end
And the SQL output is:
SELECT "space_dates".* FROM "space_dates"
INNER JOIN "space_prices" ON "space_prices"."space_date_id" = "space_dates"."id"
WHERE "space_dates"."space_id" = $1
AND "space_dates"."date" BETWEEN '2020-06-11' AND '2020-06-15'
AND (
("space_prices"."price_type" = 'hourly'
AND "space_prices"."price_cents" != 9360
OR "space_prices"."price_type" = 'daily'
AND "space_prices"."price_cents" != 66198)
OR "space_prices"."price_type" = 'monthly'
AND "space_prices"."price_cents" != 5500
) LIMIT $2
What I ended up doing was:
Creating an array of clauses based on the enum key and the price_cents
Reduced the clauses and joined them using or
Added this to the main query with an and operator and the combined_clauses

SQL join statement between table and selection in Knex

I have SQL statement :
select from resources
left join ( select resource_id, sum(price) as PostScoreSum from
prices where '2019-06-8' < dateto and '2019-06-15' >
datefrom group by resource_id ) BB on
resources.resources_id = BB.resource_id")
Using Knex, I can write this statement as knex.raw('.....'), but after this knex statement I cannot used modify (to have chain of statements, knex.raw('...').modify...is not posible). Is it possible to write this join in Knex, between table and selection without using raw.
Not clear what actually your issue is but following will generate your above query-
const sql = knex('resources')
.leftJoin((query) => {
query
.columns([
'resource_id',
knex.raw('sum(price) as PostScoreSum')
])
.from('prices')
.where('dateto', '>', '2019-06-8')
.where('datefrom', '<', '2019-06-8')
.groupBy('resources_id')
.as('BB')
}, 'resources.resources_id', 'BB.resource_id')
.toSQL();
console.log(sql) ;

Inserting a variable in a raw sql query Laravel

I am inside a function in a controller.
So from the Form, I get a value for a variable, say:
$x = "whatever";
Then I need to embed that variable (so, its value), in the WHERE statement. If I hardcode the value, it brings a correct result, but I have tried in all ways to insert that variable without success. Well, supposing that I manage to use that variable, then I will have to look into binding to avoid sql injection, but so far, I would say, see if that variable can get used in the query.
I have tried, double quotes, concatenation . $vx . , curly braces {$x}, the variable plain like this $variable, but either gives syntax errors in some cases, (concatenation), or if I just embed the variable like this where author = $x, it tells me that it can't find the column named $x
$x = "whatever";
$results = DB::select(DB::raw('SELECT
t.id, t.AvgStyle, r.RateDesc
FROM (
SELECT
p.id, ROUND(AVG(s.Value)) AS AvgStyle
FROM posts p
INNER JOIN styles s
ON s.post_id = p.id
WHERE author = $x
GROUP BY p.id
) t
INNER JOIN rates r
ON r.digit = t.AvgStyle'
));
This appears to be a simple PHP variable interpolation issue.
DB::raw() wants literally raw SQL. So there are a couple of issues that need to be fixed in the SQL string you are passing.
PHP Variable interpolation (injecting variables into a string) only happens if you use double quotes around the string. With single quotes it becomes a string constant.
If Author is a char/varchar, then SQL syntax requires quotes around the string in your raw SQL statement. Query builders typically take care of these issues for you, but you are going around them.
So the "fixed" version of this would be:
$x = "whatever";
$results = DB::select(DB::raw("SELECT
t.id, t.AvgStyle, r.RateDesc
FROM (
SELECT
p.id, ROUND(AVG(s.Value)) AS AvgStyle
FROM posts p
INNER JOIN styles s
ON s.post_id = p.id
WHERE author = '$x'
GROUP BY p.id
) t
INNER JOIN rates r
ON r.digit = t.AvgStyle"
));
Like all interpolation, this opens you up to the possibility of SQL injection if the variable being interpolated comes from user input. From the original question it is unclear whether this is a problem.
DB::select() has an option that allows you to pass an array of parameters that is inherently safe from SQL injection. In that case the solution would be:
$x = "whatever";
$results = DB::select(DB::raw("SELECT
t.id, t.AvgStyle, r.RateDesc
FROM (
SELECT
p.id, ROUND(AVG(s.Value)) AS AvgStyle
FROM posts p
INNER JOIN styles s
ON s.post_id = p.id
WHERE author = :author
GROUP BY p.id
) t
INNER JOIN rates r
ON r.digit = t.AvgStyle"
),
array('author' => $x)
);
Regarding this tutorial
$results = DB::select( DB::raw("SELECT * FROM some_table WHERE some_col = :somevariable"), array(
'somevariable' => $someVariable,
));
This is one example for you to insert variable in a raw sql laravel
$query_result = Event::select(
DB::raw('(CASE WHEN status = "draft" THEN "draft"
WHEN events.end_time <= \''.$now.'\' THEN "closed"
ELSE "available"
END) AS status'))
->orderBy('status')
->get();

Converting SQL query with inner select to sqlalchemy

I have the following query to find the oldest content_revision for each piece of content (content_revision.content_id):
select r1.id from content_revision r1
join (
select r2.content_id as content_id, min(ts_created) as min_ts_created from content_revision r2
group by r2.content_id
order by min(ts_created) desc
) as inner_result
on r1.content_id = inner_result.content_id
and r1.ts_created = inner_result.min_ts_created limit 10;
I'm trying to convert this to sqlalchemy (without running plain sql statements), but I'm not sure how to write the "on" part of my query. Here's what I've got so far:
db.session.query(RevisionModel.id)
.join(
db.session.query(RevisionModel.content_id, func.min(RevisionMode.ts_created))
.group_by(RevisionModel.content_id)
.order_by(func.min(RevisionModel.ts_created).desc())
)
??
The second argument of .join() is the join condition:
db.session.query(RevisionModel.id) \
.join(subquery, and_(subquery.c.content_id == RevisionModel.content_id,
subquery.c.min_ts_created == RevisionModel.ts_created))
You'll also need to make sure convert your sub-Query into a select with correctly labeled columns:
subquery = db.session.query(RevisionModel.content_id.label("content_id"),
func.min(RevisionModel.ts_created).label("min_ts_created")) \
.group_by(RevisionModel.content_id) \
.order_by(func.min(RevisionModel.ts_created).desc()) \
.subquery("inner_result")