Laravel query builder join using either one of two conditions - sql

I have a complex query that I want to use either Query Builder or Eloquent (preferred) but I'm struggling with an inner join.
The inner join needs to be one of either of 2 conditions so if one fails, the other is used.
This is my original query
SELECT DISTINCT tableA.crmid, tableB.*
FROM tableB
INNER JOIN tableA ON tableA.crmid = tableB.customaccountsid
INNER JOIN tableC ON (tableC.relcrmid = tableA.crmid OR tableC.crmid = tableA.crmid)
WHERE tableA.deleted = 0 AND tableC.relcrmid = 123 AND tableC.relation_id = 186
This is my attempt at using Query Builder and I know where the problem lies. It's where I join tableC. I don't know how to use my condition there
DB::table('tableB')
->join('tableA', 'tableA.crmid', '=', 'tableB.customaccountsid')
->join('tableC', function($join) {
$join->on(DB::raw('(tableC.relcrmid = tableA.crmid OR tableC.crmid = tableA.crmid)'));
})
->where('tableA.deleted', 0)
->where('tableC.relcrmid', 3727)
->where('tableC.relation_id', 186)
->select('tableA.crmid', 'tableB.*')
Ant this is the output of the query when i output as SQL
SELECT `tableA`.`crmid`, `tableB`.*
FROM `tableB`
INNER JOIN `tableA` ON `tableA`.`crmid` = `tableB`.`customaccountsid`
INNER JOIN `tableC` ON `tableC`.`relcrmid` = (tableC.relcrmid = tableA.crmid OR tableC.crmid = tableA.crmid)
WHERE `tableA`.`deleted` = ? AND `tableC`.`relcrmid` = ? AND `tableC`.`relation_id` = ?

Just try this:
->join('tableC', function ($join){
$join->on(function($query){
$query->on('tableC.relcrmid', '=', 'tableA.crmid')
->orOn('tableC.crmid', '=', 'tableA.crmid');
});
})
It returns as in your original query:
INNER JOIN tableC ON (tableC.relcrmid = tableA.crmid OR tableC.crmid = tableA.crmid)

Related

Passing different column values to where clause

SELECT pims.icicimedicalexaminerreport.id,
pims.icicimerfemaleapplicant.adversemenstrualid,
pims.icicimerfemaleapplicant.pregnantid,
pims.icicimerfemaleapplicant.miscarriageabortionid,
pims.icicimerfemaleapplicant.breastdiseaseid,
pims.pimscase.tiannumber
FROM pims.pimscase
INNER JOIN pims.digitization
ON pims.pimscase.digitizationid = pims.digitization.id
INNER JOIN pims.medicalexaminerreport
ON pims.digitization.medicalexaminerreportid =
pims.medicalexaminerreport.id
INNER JOIN pims.icicimedicalexaminerreport
ON pims.medicalexaminerreport.id =
pims.icicimedicalexaminerreport.id
INNER JOIN pims.icicimerfemaleapplicant
ON pims.icicimedicalexaminerreport.id =
pims.icicimerfemaleapplicant.id
WHERE pims.pimscase.tiannumber = 'ICICI1234567890'
which gives me the following output
Now I want to use the above output values to select the rows from the table "YesNoAnswerWithObservation"
I imagine it should look something like this Select * from YesNoAnswerWithObservation Where Id in (22,27,26,...23)
Only instead of typing the values inside IN clause I want to use the values in each column resulting from above-mentioned query.
I tried the below code but it returns all the rows in the table rather than rows mentioned inside the In
SELECT pims.yesnoanswerwithobservation.observation,
graphitegtccore.yesnoquestion.description,
pims.yesnoanswerwithobservation.id ObservationId
FROM pims.yesnoanswerwithobservation
INNER JOIN graphitegtccore.yesnoquestion
ON pims.yesnoanswerwithobservation.yesnoanswerid =
graphitegtccore.yesnoquestion.id
WHERE EXISTS (SELECT pims.icicimedicalexaminerreport.id,
pims.icicimerfemaleapplicant.adversemenstrualid,
pims.icicimerfemaleapplicant.pregnantid,
pims.icicimerfemaleapplicant.pelvicorgandiseaseid,
pims.icicimerfemaleapplicant.miscarriageabortionid,
pims.icicimerfemaleapplicant.gynocologicalscanid,
pims.icicimerfemaleapplicant.breastdiseaseid,
pims.pimscase.tiannumber
FROM pims.pimscase
INNER JOIN pims.digitization
ON pims.pimscase.digitizationid =
pims.digitization.id
INNER JOIN pims.medicalexaminerreport
ON pims.digitization.medicalexaminerreportid =
pims.medicalexaminerreport.id
INNER JOIN pims.icicimedicalexaminerreport
ON pims.medicalexaminerreport.id =
pims.icicimedicalexaminerreport.id
INNER JOIN pims.icicimerfemaleapplicant
ON pims.icicimedicalexaminerreport.id =
pims.icicimerfemaleapplicant.id
WHERE pims.pimscase.tiannumber = 'ICICI1234567890')
Any help or a nudge in the right direction would be greatly appreciated
Presumably you want the ids from the first query:
SELECT awo.observation, ynq.description, ynq.id as ObservationId
FROM pims.yesnoanswerwithobservation awo JOIN
graphitegtccore.yesnoquestion ynq
ON awo.yesnoanswerid = ynq.id
WHERE ynq.id = (SELECT mer.id
FROM pims.pimscase c JOIN
pims.digitization d
ON c.digitizationid = d.id JOIN
pims.medicalexaminerreport mer
ON d.medicalexaminerreportid = mer.id JOIN
pims.icicimedicalexaminerreport imer
ON mer.id = imer.id JOIN
pims.icicimerfemaleapplicant ifa
ON imer.id = ifa.id
WHERE c.tiannumber = 'ICICI1234567890'
) ;
Notice that table aliases make the query much easier to write and to read.

How do I fix the syntax of a sub query with joins?

I have the following query:
SELECT tours_atp.NAME_T, today_atp.TOUR, today_atp.ID1, odds_atp.K1, today_atp.ID2, odds_atp.K2
FROM (players_atp INNER JOIN (players_atp AS players_atp_1 INNER JOIN (today_atp INNER JOIN odds_atp ON (today_atp.TOUR = odds_atp.ID_T_O) AND (today_atp.ID1 = odds_atp.ID1_O) AND (today_atp.ID2 = odds_atp.ID2_O) AND (today_atp.ROUND = odds_atp.ID_R_O)) ON players_atp_1.ID_P = today_atp.ID2) ON players_atp.ID_P = today_atp.ID1) INNER JOIN tours_atp ON today_atp.TOUR = tours_atp.ID_T
WHERE (((tours_atp.RANK_T) Between 1 And 4) AND ((today_atp.RESULT)="") AND ((players_atp.NAME_P) Not Like "*/*") AND ((players_atp_1.NAME_P) Not Like "*/*") AND ((odds_atp.ID_B_O)=2))
ORDER BY tours_atp.NAME_T;
I'd like to add a field to this query that provides me with the sum of a field in another table (FS) with a few criteria applied.
I've been able to build a stand alone query to get the sum of FS by ID_T as follows:
SELECT tbl_Ts_base_atp.ID_T, Sum(tbl_Ts_mkv_atp.FS) AS SumOfFS
FROM tbl_Ts_base_atp INNER JOIN tbl_Ts_mkv_atp ON tbl_Ts_base_atp.ID_Ts = tbl_Ts_mkv_atp.ID_Ts
WHERE (((tbl_Ts_base_atp.DATE_T)>Date()-2000 And (tbl_Ts_base_atp.DATE_T)<Date()))
GROUP BY tbl_Ts_base_atp.ID_T, tbl_Ts_mkv_atp.ID_Ts;
I now want to match up the sum of FS from the second query to the records of the first query by ID_T. I realise I need to do this using a sub query. I'm confident using these when there's only one table but I consistently get 'syntax errors' when there are joins.
I simplified the first query down to remove all the WHERE conditions so it was easier for me to try and error check but no luck. I guess the resulting SQL will also be easier for you guys to follow:
SELECT today_atp.TOUR, (SELECT Sum(tbl_Ts_mkv_atp.FS)
FROM tbl_Ts_mkv_atp INNER JOIN (tbl_Ts_base_atp INNER JOIN today_atp ON tbl_Ts_base_atp.ID_T = today_atp.TOUR) ON tbl_Ts_mkv_atp.ID_Ts = tbl_Ts_base_atp.ID_Ts AS tt
WHERE tt.DATE_T>Date()-2000 And tt.DATE_T<Date() AND tt.TOUR=today_atp.TOUR
ORDER BY tt.DATE_T) AS SumOfFS
FROM today_atp
Can you spot where I'm going wrong? My hunch is that the issue is in the FROM line of the sub query but I'm not sure. Thanks in advance.
It's difficult to advise an appropriate solution without knowledge of how the database tables relate to one another, but assuming that I've correctly understood what you are looking to achieve, you might wish to try the following solution:
select
tours_atp.name_t,
today_atp.tour,
today_atp.id1,
odds_atp.k1,
today_atp.id2,
odds_atp.k2,
subq.sumoffs
from
(
(
(
(
today_atp inner join odds_atp on
today_atp.tour = odds_atp.id_t_o and
today_atp.id1 = odds_atp.id1_o and
today_atp.id2 = odds_atp.id2_o and
today_atp.round = odds_atp.id_r_o
)
inner join players_atp as players_atp_1 on
players_atp_1.id_p = today_atp.id2
)
inner join players_atp on
players_atp.id_p = today_atp.id1
)
inner join tours_atp on
today_atp.tour = tours_atp.id_t
)
inner join
(
select
tbl_ts_base_atp.id_t,
sum(tbl_ts_mkv_atp.fs) as sumoffs
from
tbl_ts_base_atp inner join tbl_ts_mkv_atp on
tbl_ts_base_atp.id_ts = tbl_ts_mkv_atp.id_ts
where
tbl_ts_base_atp.date_t > date()-2000 and tbl_ts_base_atp.date_t < date()
group by
tbl_ts_base_atp.id_t
) subq on
tours_atp.tour = subq.id_t
where
(tours_atp.rank_t between 1 and 4) and
today_atp.result = "" and
players_atp.name_p not like "*/*" and
players_atp_1.name_p not like "*/*" and
odds_atp.id_b_o = 2
order by
tours_atp.name_t;

How to use subquery in the join function of Yii framework 2 ActiveRecord?

Below is my pure SQL query.
SELECT a.*, b.*
FROM a
INNER JOIN b
ON a.id = b.a_id
INNER JOIN (
SELECT a_id, MAX(add_time) AS max_add_time
FROM b
GROUP BY a_id
) m
ON b.a_id = m.a_id AND b.add_time = m.max_add_time
ORDER BY b.add_time DESC
I have the subquery in the second INNER JOIN. Below my active query.
$subQuery = B::find()->select(['a_id', 'MAX(add_time) AS max_add_time'])->groupBy('a_id');
$query = A::find()->innerJoin('b', 'a.id = b.a_id')
->innerJoin('(' .
$subQuery->prepare(Yii::$app->db->queryBuilder)
->createCommand()
->rawSql
. ') m', 'b.a_id = m.a_id AND a.add_time = m.max_add_time ')
->orderBy('b.add_time DESC');
It works fine, but I do not like the way I use the subquery in the second INNER JOIN. What I want to approach with this query is to select the left table inner join with right table, group by a_id and order by the add_time (DESC) of the right table. How should I better use the subquery in the second INNER JOIN?
The snippet below is untested but it should be something like that. If you read the docs (at http://www.yiiframework.com/doc-2.0/yii-db-query.html#innerJoin()-detail) you can see an array with a subquery is also valid input, with the key being the alias.
$subQuery = B::find()
->select(['a_id', 'MAX(add_time) AS max_add_time'])
->groupBy('a_id');
$query = A::find()
->innerJoin('b', 'a.id = b.a_id')
->innerJoin(['m' => $subQuery], 'b.a_id = m.a_id AND a.add_time = m.max_add_time')
->orderBy('b.add_time DESC');
Having created the join, if you need to use any of the columns returned by the subQuery, you need to add properties to the Yii2 model class, e.g.
$subQuery = FinancialTransaction::find()
->select( new \yii\db\Expression( 'SUM(amount) as owing') )
->addSelect('booking_id')
->groupBy('booking_id');
$query = $query
->addSelect(['b.*', 'owing'])
->leftJoin(['ft' => $subQuery], 'b.booking_display_id = ft.booking_id');
To access "owing" the model has to have the property (PHPdoc optional):
/**
* #var float
*/
public $owing=0;

SQL statement and LINQ returning different amount of data

I'm trying to convert a SQL statement to LINQ and for some reason the result sets I'm getting are different. The SQL is returning more data than the LINQ.
This is the SQL Query:
SELECT DISTINCT ssLookup.StopID,
dc.CityName,
dc.StateAbbrev,
sc.CarrierID,
sc.CarrierCode,
sc.CarrierName
FROM ScheduleDestinationCache dc
INNER JOIN ScheduleStops ssFilter ON ssFilter.CarrierID = dc.CarrierID
INNER JOIN ScheduleStops ssLookup ON dc.CityID = ssLookup.StopID
INNER JOIN ScheduleCarriers sc ON ssLookup.CarrierID = sc.CarrierID
WHERE (ssFilter.StopID = 582 OR ssFilter.StopID IN
(SELECT * FROM dbo.GetTwins(582)))
ORDER BY dc.CityName, dc.StateAbbrev
This is the LINQ statement:
(from sdc in ScheduleDestinationCaches
let twins = GetTwins(582).Select(gt => gt.StopIDs)
join ssFilter in ScheduleStops on sdc.CarrierID equals ssFilter.CarrierID
join ssLookup in ScheduleStops on sdc.CityID equals ssLookup.CityID
join sc in ScheduleCarriers on ssLookup.CarrierID equals sc.CarrierID
where ssFilter.StopID == 582 || twins.Contains(ssFilter.StopID)
orderby sdc.CityName, sdc.StateAbbrev
select new {
ssLookup.StopID,
sdc.CityName,
sdc.StateAbbrev,
sc.CarrierID,
sc.CarrierCode,
sc.CarrierName
}).GroupBy(g => new
{
g.StopID,
g.CityName,
g.StateAbbrev,
g.CarrierID,
g.CarrierCode,
g.CarrierName
})
What am I doing wrong?
Thank you in advance.

Join with Zend_Db, without having the columns of the joined tables

I use Zend_Db_Select to perform a query with a Join. I end up with the following SQL query :
SELECT `Utilisateur`.*, `Ressource`.*, `Acl_Cache`.*, `Role`.*, `UtilisateurRole`.* FROM `Utilisateur`
INNER JOIN `Ressource` ON Ressource.idJointure = Utilisateur.id
INNER JOIN `Acl_Cache` ON Acl_Cache.idRessource = Ressource.id
INNER JOIN `Role` ON Role.id = Acl_Cache.idRole
INNER JOIN `UtilisateurRole` ON UtilisateurRole.idRole = Role.id
WHERE (Ressource.tableJointure = 'Utilisateur') AND (UtilisateurRole.idUtilisateur = '2')
Why is Zend_Db adding this part is the SELECT clause :
, `Ressource`.*, `Acl_Cache`.*, `Role`.*, `UtilisateurRole`.*
I never asked this, and I don't want that. How to prevent this behavior ?
$db->select()
->from(array('alias1'=>'table_i_want_all_cols_on'))
->joinLeft(array('alias2'=>'table_i_want_no_cols_on'),
'alias1.id = alias2.id',
array()
)
->joinLeft(array('alias3'=>'table_i_want_some_cols_on'),
'alias3.id = alias1.id',
array('col1', 'col2')
);