Laravel 5, Derived table in join clause? - left-join

I have this query:
SELECT * FROM blog
LEFT JOIN (
SELECT blog_id, AVG(value) as blog_rating FROM blog_ratings
GROUP BY (blog_id)
) T ON T.blog_id = blog.id;
I do not know how to write this with Eloquent.
For Example:
Blog::select("*")->leftJoin( /* Here goes derived table */ )->get()
How do I accomplish this?

I'd personally just use the fluent query builder, try this out and see how it works out:
DB::table('blog')
->select('*')
->leftJoin(DB::raw('(SELECT blog_id, AVG(value) as blog_rating FROM blog_ratings
GROUP BY (blog_id)
) as T'), function ($join) {
$join->on ( 'T.blog_id', '=', 'blog.id' );
})
->get();
You can always swap ->get() for ->toSql() to dump out the query and adjust if you see any mistakes.

Related

TYPO3 DBAL Querybuilder: Nested SELECT statements?

Is it possible to build nested SELECT statements like the one below using the DBAL QueryBuilder?
SELECT i.id, i.stable_id, i.version, i.title
FROM initiatives AS i
INNER JOIN (
SELECT stable_id, MAX(version) AS max_version FROM initiatives GROUP BY stable_id
) AS tbl1
ON i.stable_id = tbl1.stable_id AND i.version = tbl1.max_version
ORDER BY i.stable_id ASC
The goal is to query an external non TYPO3 table which contains different versions of each data set. Only the data set with the highest version number should be rendered. The database looks like this:
id, stable_id, version, [rest of the data row]
stable_id is the external id of the data set. id is the internal autoincrement id. And version is also incremented automatically.
Code example:
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
$result = $queryBuilder
->select(...$this->select)
->from($this->table)
->join(
'initiatives',
$queryBuilder
->select('stable_id, MAX(version) AS max_version' )
->from('initiatives')
->groupBy('stable_id'),
'tbl1',
$queryBuilder->and(
$queryBuilder->expr()->eq(
'initiatives.stable_id',
$queryBuilder->quoteIdentifier('tbl1.stable_id')
),
$queryBuilder->expr()->eq(
'initiatives.version',
$queryBuilder->quoteIdentifier('tbl1.max_version')
)
)
)
->orderBy('stable_id', 'DESC')
I cannot figure out the correct syntax for the ON ... AND statement. Any idea?
Extbase queries have JOIN capabilities but are otherwise very limited. You could use custom SQL (see ->statement() here), though.
A better API to build complex queries is the (Doctrine DBAL) QueryBuilder, including support for JOINs, database functions like MAX() and raw expressions (->addSelectLiteral()). Make sure to read until the ExpressionBuilder where it gets interesting.
So Extbase queries are useful in order to retrieve Extbase (model) objects. It can make implicit use of its knowledge of your data structure in order to save you some code but only supports rather simple queries.
The (Doctrine DBAL) QueryBuilder fulfills all other needs. If needed, you can convert the raw data to Extbase models, too. (for example $propertyMapper->convert($data, Job::class)).
I realize that we lack clear distinguishing between the two because they were both known at some time as "QueryBuilder", but they are totally different. That's why I like to add "Doctrine" when referring to the non-Extbase one.
An example with a JOIN ON multiple criteria.
$q = TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(TYPO3\CMS\Core\Database\ConnectionPool::class)
->getQueryBuilderForTable('fe_users');
$res = $q->select('*')
->from('tt_content', 'c')
->join(
'c',
'be_users',
'bu',
$q->expr()->andX(
$q->expr()->eq(
'c.cruser_id', $q->quoteIdentifier('bu.uid')
),
$q->expr()->comparison(
'2', '=', '2'
)
)
)
->setMaxResults(5)
->execute()
->fetchAllAssociative();
Short answer: it is not possible because the table to be joined in is generated on the fly. The related expression is back-ticked and thus causes an SQL error.
But: The SQL query can be changed to the following SQL query which does basically the same:
SELECT i1.id,stable_id, version, title, p.name, l.name, s.name
FROM initiatives i1
WHERE version = (
SELECT MAX(i2.version)
FROM initiatives i2
WHERE i1.stable_id = i2.stable_id
)
ORDER BY stable_id ASC
And this can be rebuild with the DBAL queryBuilder:
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
$result = $queryBuilder
->select(...$this->select)
->from($this->table)
->where(
$queryBuilder->expr()->eq(
'initiatives.version',
'(SELECT MAX(i2.version) FROM initiatives i2 WHERE initiatives.stable_id = i2.stable_id)'
),
->orderBy('stable_id', 'DESC')
->setMaxResults( 50 )
->execute();

querydsl exists duplicate condition

here is my full sql as
new BooleanBuilder(
qClaimDataEntity.companyId.eq(companyId)
.or(
qClaimDataEntity.projectDataEntity.remoteProjectDataEntityList
.any().assignedCompanyId.eq(companyId)
)
.or(
qClaimDataEntity.projectDataEntity.remoteProjectDataEntityList
.any().remoteProjectStatus.eq(RemoteProjectStatusEnum.合作中)
)
);
it print sql string as
select
claimdatae0_.*
from
claim_data claimdatae0_
where
claimdatae0_.company_id=?
or exists (
select
1
from
project_data projectdat1_ cross
join
remote_project_data remoteproj2_
where
claimdatae0_.project_data_id=projectdat1_.project_data_id
and projectdat1_.project_data_id=remoteproj2_.project_data_id
and remoteproj2_.assigned_company_id=?
)
or exists (
select
1
from
project_data projectdat3_ cross
join
remote_project_data remoteproj4_
where
claimdatae0_.project_data_id=projectdat3_.project_data_id
and projectdat3_.project_data_id=remoteproj4_.project_data_id
and remoteproj4_.remote_project_status=?
) limit ?
but I think it can be done as
select
claimdatae0_.*
from
claim_data claimdatae0_
where
claimdatae0_.company_id=?
or exists (
select
1
from
project_data projectdat1_ cross
join
remote_project_data remoteproj2_
where
claimdatae0_.project_data_id=projectdat1_.project_data_id
and projectdat1_.project_data_id=remoteproj2_.project_data_id
and remoteproj2_.assigned_company_id=?
and remoteproj2_.remote_project_status=?
) limit ?
SubQuery is not support in latest Spring data jpa
and querydsl .any() don't support duplicate condition
is there another way to get the second query using querydsl?
The example is two separate or statements. I think the or should be nested, like this:
new BooleanBuilder(
qClaimDataEntity.companyId.eq(companyId)
.or(
qClaimDataEntity.projectDataEntity.remoteProjectDataEntityList
.any().assignedCompanyId.eq(companyId)
.or(
qClaimDataEntity.projectDataEntity.remoteProjectDataEntityList
.any().remoteProjectStatus.eq(RemoteProjectStatusEnum.合作中)
)
));

Subjquery outside of Join

code downstairs will perfom query like this
SELECT anrede FROM `l_anrede` INNER JOIN `person` ON `l_anrede`.id = `person`.id_anrede
But I need a query like this:
SELECT anrede FROM `l_anrede` INNER JOIN `person` ON `l_anrede`.id=`person`.id_anrede WHERE
person.id IN(SELECT id_person FROM bewerber WHERE person.id=bewerber.id);
Any ideas how to round out this code:
$query = LAnrede::find();
/*
$subQuery = Bewerber::find()->select(['id_person'])
->from('bewerber')->where('person.id= bewerber.id');*/
$query->select('anrede')->from('l_anrede')->innerJoin('Person', 'l_anrede.id = person.id_anrede');
var_dump($query->one());
You don't have to use ->from('l_anrede') if youre calling LAnrede::find() - query will select data from table associated with your model. To use subquery in where() method, try like this:
$query->andWhere(['person.id' => $subQuery]);

postgresql how to use boolean in where statement

I'm a really bad in sql , my query is
select * from car_wash where
(select ST_Within((select car_wash.lon_lat from car_wash),(select ST_Buffer(ST_GeomFromText('POINT(65.3 323.2)'),20)))) = true
AND car_wash.was_deleted=false;
But i know that it isn't correct because nested query can return more than 1 column, how to modify this query to use where clause
select *
from car_wash cw
where
ST_Within (
cw.lon_lat,
ST_Buffer(ST_GeomFromText('POINT(65.3 323.2)'),20)
)
AND
not car_wash.was_deleted
I don't use postgresql, but maybe something like this works:
select * from car_wash
where EXISTS (select ST_Within((select car_wash.lon_lat from car_wash),
(select ST_Buffer(ST_GeomFromText('POINT(65.3 323.2)'),20))) within
WHERE within = true)
AND car_wash.was_deleted=false;
If it doesn't work, I have a variant, so tell me when.

add multi tables in from() zend_db_select

i have to do a select from multi tables using zend_db_select for this sql :
SELECT t1.id,t2.ids, t3.uid
FROM table1 t1,table2 t2, table3 t3
this is the code used :
$subQuery = $this->getDbTable ()->select ()->setIntegrityCheck ( false )
->from(array('t1'=>'table1','t2'=>'table2','t3'=>'table3'),array('t1.id','t2.ids','t3.uid'))
->query()
->fetchAll();
so i have message error say that t2.ids is not in the column list
because zend_db_select take just the first table
any solution to resolve this problème ?
thaks
You can use a cross join with:
$subQuery = $this->getDbTable ()->select ()->setIntegrityCheck ( false )
->from(array('t1'=>'table1'),array('t1.id'))
->joinCross(array('t2'=>'table2'),array('t2.ids'))
->joinCross(array('t3'=>'table3'),array('t3.uid'))
->query()
->fetchAll();