Left join with result of COALESCE - sql

SELECT *
FROM TBDA temp
LEFT JOIN (
SELECT
(
COALESCE (
(
SELECT sum_payment
FROM TBDA_REPAYMENT
WHERE isdn = temp.isdn
AND tr_month = temp.tr_month
AND tr_year = temp.tr_year
),
(
SELECT sum_payment
FROM TBDA_PAYNEW
WHERE isdn = temp.isdn
AND tr_month = temp.tr_month
AND tr_year = temp.tr_year
)
)
)
)
TBDA table has isdn,tr_month,tr_year
TBDA_REPAYMENT table has isdn,tr_month,tr_year,sum_payment
TBDA_PAYNEW table has isdn,tr_month,tr_year,sum_payment
Expected result :
A table off all fields of TBDA with sum_payment, look for sum_payment from two tables (TBDA_REPAYMENT & TBDA_PAYNEW) by ISDN (TBDA table) by comparing isdn,tr_month,tr_year, if sum_payment exists in one of two tables (TBDA_REPAYMENT & TBDA_PAYNEW) then add it to the results, otherwise it's null.
I get the error :
[Err] 42000 - [SQL Server]Incorrect syntax near ')'.

SELECT
*
FROM
TBDA temp
Cross Apply ( SELECT COALESCE (
(
SELECT
sum_payment
FROM
TBDA_REPAYMENT
WHERE
isdn = temp.isdn
AND tr_month = temp.tr_month
AND tr_year = temp.tr_year
),
(
SELECT
sum_payment
FROM
TBDA_PAYNEW
WHERE
isdn = temp.isdn
AND tr_month = temp.tr_month
AND tr_year = temp.tr_year
)
) as First_Null_Value
) as value_table

Unless a CROSS JOIN, CROSS APPLY or OUTER APPLY is used, the ON clause is required.
Hence the error.
Although you could still do something like ... LEFT JOIN othertable ON (1=1).
But you could re-write this query using 2 left joins.
select
temp.*,
coalesce(repay.sum_payment, paynew.sum_payment) as value_table
from TBDA temp
left join TBDA_REPAYMENT repay on (repay.isdn = temp.isdn and repay.tr_month = temp.tr_month and repay.tr_year = temp.tr_year)
left join TBDA_PAYNEW paynew on (paynew.isdn = temp.isdn and paynew.tr_month = temp.tr_month and paynew.tr_year = temp.tr_year);
Note that there's an assumption that the tables TBDA_REPAYMENT & TBDA_PAYNEW have a uniqueness on the 3 fields used in the join (isdn, tr_month, tr_year).
Or the OUTER APPLY syntax could be used.
And if there's no uniqueness on (isdn, tr_month, tr_year) then you can use that to SUM the total at the same time.
select
temp.*, repay.sum_payment as repay_sum_payment, paynew.sum_payment as paynew_sum_payment,
coalesce(repay.sum_payment, paynew.sum_payment) as value_table
from TBDA temp
outer apply
(
select nullif(sum(rp.sum_payment),0) as sum_payment
from TBDA_REPAYMENT rp
where rp.isdn = temp.isdn
and rp.tr_month = temp.tr_month
and rp.tr_year = temp.tr_year
) as repay
outer apply
(
select nullif(sum(pn.sum_payment),0) as sum_payment
from TBDA_PAYNEW pn
where pn.isdn = temp.isdn
and pn.tr_month = temp.tr_month
and pn.tr_year = temp.tr_year
) as paynew;
Example Snippet:
declare #TBDA table (isdn int, tr_month int, tr_year int);
declare #TBDA_REPAYMENT table (isdn int, tr_month int, tr_year int, sum_payment int);
declare #TBDA_PAYNEW table (isdn int, tr_month int, tr_year int, sum_payment int);
insert into #TBDA (isdn, tr_month, tr_year) values (1,6,2018),(2,6,2018);
insert into #TBDA_REPAYMENT (isdn, tr_month, tr_year, sum_payment) values (1,6,2018, 100);
insert into #TBDA_PAYNEW (isdn, tr_month, tr_year, sum_payment) values (1,6,2018, 200),(2,6,2018,100),(2,6,2018,200);
--
-- Using left join
--
select
temp.*, repay.sum_payment, paynew.sum_payment,
coalesce(repay.sum_payment, paynew.sum_payment) as value_table
from #TBDA temp
left join #TBDA_REPAYMENT repay on (repay.isdn = temp.isdn and repay.tr_month = temp.tr_month and repay.tr_year = temp.tr_year)
left join #TBDA_PAYNEW paynew on (paynew.isdn = temp.isdn and paynew.tr_month = temp.tr_month and paynew.tr_year = temp.tr_year);
--
-- Using outer apply
--
select
temp.*, repay.sum_payment as repay_sum_payment, paynew.sum_payment as paynew_sum_payment,
coalesce(repay.sum_payment, paynew.sum_payment) as value_table
from #TBDA temp
outer apply
(
select nullif(sum(rp.sum_payment),0) as sum_payment
from #TBDA_REPAYMENT rp
where rp.isdn = temp.isdn
and rp.tr_month = temp.tr_month
and rp.tr_year = temp.tr_year
) as repay
outer apply
(
select nullif(sum(pn.sum_payment),0) as sum_payment
from #TBDA_PAYNEW pn
where pn.isdn = temp.isdn
and pn.tr_month = temp.tr_month
and pn.tr_year = temp.tr_year
) as paynew;

Error is due to not specifying ON condition on LEFT JOIN.
LEFT JOIN without ON will throw an error.
If you don't want to specify any condition, try CROSS JOIN

Just use correlated subqueries:
SELECT t.*,
IFNULL ( (SELECT r.sum_payment
FROM TBDA_REPAYMENT r
WHERE r.isdn = t.isdn AND
r.tr_month = t.tr_month AND
r.tr_year = t.tr_year
),
(SELECT p.sum_payment
FROM TBDA_PAYNEW p
WHERE p.isdn = t.isdn AND
p.tr_month = t.tr_month AND
p.tr_year = t.tr_year
)
)
)
FROM TBDA t;
Although you can use APPLY, the use of SELECT is already insisting that at most one row be returned from the subqueries.
Also note a couple of things. First, I replaced COALESCE() with IFNULL(). Although I much prefer COALESCE() (because it is the standard and more flexible), the code is more efficient with IFNULL(). COALESCE() causes SQL Server to evaluate the first argument twice when it is not NULL.
Second, table aliases and qualified column names should be used everywhere in the query.

Related

SQL Query from multiple tables show all records

Please advise me on what I am doing wrong with query below
Scenario:
Data is in Three tables, One table contains complete record, other two tables have missing records, I want a query to based on Table A(where all records are present) and other two tables show result accordingly just like excel ( index match ) problem is it is filtering data correctly but shows only those records where all data matches in all three tables, I want query to show data even if it is available only in only one table.
Query I have generated so far:
SELECT primaryitemtable.itemcode,
primaryitemtable.itemdescription,
primaryitemtable.article,
itembatchlist.batchno,
Sum(tempscmstock.qty) AS SumOfQty,
Sum(stockmastertemp.qty) AS SumOfQty1,
tempscmstock.batch,
stockmastertemp.batch
FROM (stockmastertemp
INNER JOIN (tempscmstock
INNER JOIN primaryitemtable
ON tempscmstock.itemcode =
primaryitemtable.itemcode)
ON ( stockmastertemp.itemcode = primaryitemtable.itemcode )
AND ( stockmastertemp.itemcode = primaryitemtable.itemcode ))
INNER JOIN itembatchlist
ON primaryitemtable.itemcode = itembatchlist.itemcode
GROUP BY primaryitemtable.itemcode,
primaryitemtable.itemdescription,
primaryitemtable.article,
itembatchlist.batchno,
tempscmstock.batch,
stockmastertemp.batch
HAVING ( ( ( tempscmstock.batch ) = [itembatchlist] ! [batchno] )
AND ( ( stockmastertemp.batch ) = [itembatchlist] ! [batchno] ) )
ORDER BY primaryitemtable.itemcode;
[]
Try Left join because inner join return only those records where every table has same data
SELECT primaryitemtable.itemcode,
primaryitemtable.itemdescription,
primaryitemtable.article,
itembatchlist.batchno,
Sum(tempscmstock.qty) AS SumOfQty,
Sum(stockmastertemp.qty) AS SumOfQty1,
tempscmstock.batch,
stockmastertemp.batch
FROM (stockmastertemp
LEFT JOIN (tempscmstock
LEFT JOIN primaryitemtable
ON tempscmstock.itemcode = primaryitemtable.itemcode)
ON ( stockmastertemp.itemcode = primaryitemtable.itemcode )
AND ( stockmastertemp.itemcode = primaryitemtable.itemcode ))
LEFT JOIN itembatchlist
ON primaryitemtable.itemcode = itembatchlist.itemcode
GROUP BY primaryitemtable.itemcode,
primaryitemtable.itemdescription,
primaryitemtable.article,
itembatchlist.batchno,
tempscmstock.batch,
stockmastertemp.batch
HAVING ( ( ( tempscmstock.batch ) = [itembatchlist] ! [batchno] )
AND ( ( stockmastertemp.batch ) = [itembatchlist] ! [batchno] ) )
ORDER BY primaryitemtable.itemcode

Existing query optimization

We have 5 tables and we are trying to create a view to get the results.
Below is the view which is working fine.
I need suggestions. Is it a good practice to write this query in this way or it can be optimized in a better way.
SELECT p.Pid, hc.hcid, hc.Accomodation, ghc.ghcid, ghc.ProductFeatures, wp.existing, wp.acute, mc.cardiaccover, mc.cardiaclimitationperiod
FROM TableA p
LEFT JOIN TableB hc
ON p.pid = hc.pid
LEFT JOIN TableC ghc
ON p.pid = ghc.pid
LEFT JOIN (SELECT *
FROM (SELECT hcid,
title,
wperiodvalue + '-' + CASE WHEN
wperiodvalue > 1 THEN
unit +
's' ELSE
unit END wperiod
FROM TableD) d
PIVOT ( Max(wperiod)
FOR title IN (acute,
existing
) ) piv1) wp
ON hc.hcid = wp.hcid
LEFT JOIN (SELECT *
FROM (SELECT hcid,
title + col new_col,
value
FROM TableE
CROSS apply ( VALUES (cover,
'Cover'),
(Cast(limitationperiod AS
VARCHAR
(10)),
'LimitationPeriod') ) x (value, col
)) d
PIVOT ( Max(value)
FOR new_col IN (cardiaccover,
cardiaclimitationperiod,
cataracteyelenscover,
cataracteyelenslimitationperiod
) ) piv2) mc
ON hc.hcid = mc.hcid
Any suggestions would be appreciated.
Thanks
My suggestion is to break down the query using temporary table, create stored procedure then dump data in the one new table and with the help of that table you can create view:
Store both PIVOT result in tow seperate temp tables as
SELECT * INTO #pvtInfo FROM ( --first PIVOT query
SELECT * INTO #pvtInfoTwo FROM ( --second PIVOT query
Then your final query will be as :
SELECT p.Pid,
hc.hcid,
hc.Accomodation,
ghc.ghcid,
ghc.ProductFeatures,
wp.existing,
wp.acute,
mc.cardiaccover,
mc.cardiaclimitationperiod
FROM TableA p
LEFT JOIN TableB hc ON p.pid = hc.pid
LEFT JOIN TableC ghc ON p.pid = ghc.pid
LEFT JOIN #pvtInfo wp ON hc.hcid = wp.hcid
LEFT JOIN #pvtInfoTwo mc ON hc.hcid = mc.hcid
First you can try then only go with SP and VIEW.
Hope, It will help.

changing temp tables to declaring them

Just a question regards to temp tables and declaring table. If I change the temp tables 'ChangedData' and 'PackageDatatoProcess' to their own variables '#ChangedData' and '#PackageDatatoProcess', can I ask how I am suppose to change the select into statement as I have not quite done this before. Virtually I told that we can declare tables rather than using the select into but just need a bit of help with this:
select distinct * into #PackageDataToProcess from #ChangedData pp
outer apply (
select pk.Reference, pjl.PackageToJournalLinkId, j.CreatedDate, pccl.PackageCostChangeLogId from Jet2Holidays.dbo.Package pk
inner join Jet2Holidays.dbo.PackageToJournalLink pjl on pk.PackageId = pjl.PackageId
inner join Jet2Holidays.dbo.Journal j on pjl.JournalId = j.JournalId
and j.PrincipalName= iif(#AllowNonSupportChanges = 0, 'HolidaysSupport', j.PrincipalName)
inner join Jet2Holidays.dbo.BusinessProcess bp on pjl.BusinessProcessId = bp.BusinessProcessId
and bp.[Description] = iif(#AllowNonSupportChanges = 0, 'CallCentreAction', bp.[Description])
left outer join Jet2Holidays.dbo.PackageCostChangeLog pccl on pccl.PackageToJournalLinkId = pjl.PackageToJournalLinkId
where pk.Reference = pp.PackageReference
and pp.JournalID = pjl.JournalId
) as packageData
First, you declare your variable tables like so:
DECLARE #PackageDataToProcess TABLE
(
Reference UNIQUEIDENTIFIER
, PackageToJournalLinkId INT
, CreatedDate DATETIME
, PackageCostChangeLogId INT
, {other columns here}
)
DECLARE #ChangedData TABLE
(
Reference UNIQUEIDENTIFIER
, PackageToJournalLinkId INT
, CreatedDate DATETIME
, PackageCostChangeLogId INT
, {other columns here}
)
At this point you can populate your #ChangedData table like so:
INSERT #ChangedData ( Reference, PackageToJournalLinkId, CreatedDate, PackageCostChangeLogId, {other columns})
SELECT Reference, PackageToJournalLinkId, CreatedDate, PackageCostChangeLogId, {other columns}
FROM ChangedDataSource -- Table, Procedure, Function
And then you can run your code by substituting #PackageDataToProcess for #PackageDataToProcess. Here it is again with a slight re-write (from the original):
INSERT #PackageDataToProcess
select distinct pp.* from #ChangedData pp
outer apply (
select pk.Reference, pjl.PackageToJournalLinkId, j.CreatedDate, pccl.PackageCostChangeLogId from Jet2Holidays.dbo.Package pk
inner join Jet2Holidays.dbo.PackageToJournalLink pjl on pk.PackageId = pjl.PackageId
inner join Jet2Holidays.dbo.Journal j on pjl.JournalId = j.JournalId
and j.PrincipalName= iif(#AllowNonSupportChanges = 0, 'HolidaysSupport', j.PrincipalName)
inner join Jet2Holidays.dbo.BusinessProcess bp on pjl.BusinessProcessId = bp.BusinessProcessId
and bp.[Description] = iif(#AllowNonSupportChanges = 0, 'CallCentreAction', bp.[Description])
left outer join Jet2Holidays.dbo.PackageCostChangeLog pccl on pccl.PackageToJournalLinkId = pjl.PackageToJournalLinkId
where pk.Reference = pp.PackageReference
and pp.JournalID = pjl.JournalId
) as packageData
Your declared tabled will go out of scope in a similar manner to the way your non-global temporary table do.

How to check intersection of subqueries in query?

I have the next query:
SELECT c.client_code, a.account_num, m.account_close_date, u.uso, m.product_name
FROM accounts a INNER JOIN Clients c ON c.id = a.client_id INNER JOIN
Uso u ON c.uso_id = u.uso_id INNER JOIN Magazine m ON a.account_id = m.account_id
and I need to compare product_name with input parameter.
product_name and input parameter #s are comma-delimited strings.
I use next split function:
ALTER FUNCTION [dbo].[Split]
(
#s VARCHAR(max),
#split CHAR(1)
)
RETURNS #temptable TABLE (items VARCHAR(MAX))
AS
BEGIN
DECLARE #x XML
SELECT #x = CONVERT(xml,'<root><s>' + REPLACE(#s,#split,'</s><s>') + '</s></root>');
INSERT INTO #temptable
SELECT [Value] = T.c.value('.','varchar(20)')
FROM #X.nodes('/root/s') T(c);
RETURN
END;
I think that I need to check the intersection of tables, which I will receive after split of product_name and after split of input parameter. I trid to do this:
WHERE (select * from dbo.Split(m.product_name, ';')
INTERSECT select * from dbo.Split('product1;product2',';'))
is not null
But it does not work quite right. Please, help me.
INTERSECT requires the same column output and is used like UNION or EXCEPT: not in the WHERE clause
Just JOIN onto the udf
...
INNER JOIN
Magazine m ON a.account_id = m.account_id
INNER JOIN
dbo.Split(#parameter, ';') CSV ON m.productname = CSV.items
If you need to split m.productname, if you can't fix the design, use CROSS APPLY
...
INNER JOIN
Magazine m ON a.account_id = m.account_id
CROSS APPLY
dbo.Split(m.productname, ';') WTF
INNER JOIN
dbo.Split(#parameter, ';') CSV ON WTF.items = CSV.items
However, JOIN and INTERSECT give different results if #parameter has duplicated values. Add a DISTINCT to the UDF for example to get around this. Or change the udf JOIN into EXISTS

"Where IN" with multiple columns (SQL Server)

I'm using SQL Server 2005.
The query would look like this
Select col1, col2, col3 from <subquery> where (col1,col2) in <subquery>
SQL Server doesn't seem to like that. Any way of implementing that that anyone knows of that doesn't involve converting to varchars or anything else messy?
This is the actual query.
SELECT *
FROM
(
SELECT NEWID() AS guid, UserID, RoleId, ClubId, 0 AS GroupRole
FROM dbo.Portal_UserRoles
UNION
SELECT NEWID() AS guid, dbo.Portal_UserGroups.UserId,
dbo.Portal_GroupRoles.RoleId, dbo.Portal_UserGroups.ClubId,
dbo.Portal_GroupRoles.GroupId AS GroupRole
FROM dbo.Portal_GroupRoles
INNER JOIN dbo.Portal_UserGroups
ON dbo.Portal_GroupRoles.GroupId = dbo.Portal_UserGroups.GroupId
) AS derivedtbl_1
WHERE (derivedtbl_1.RoleId,derivedtbl_1.ClubId) IN
(
SELECT Portal_GroupRoles.RoleId, Portal_ClubGroups.ClubId
FROM Portal_GroupRoles
INNER JOIN Portal_ClubGroups
ON Portal_GroupRoles.GroupId = Portal_ClubGroups.GroupId
)
The standard, classic way to do what you seek is an EXISTS clause:
SELECT *
FROM
(
SELECT NEWID() AS guid, UserID, RoleId, ClubId, 0 AS GroupRole
FROM dbo.Portal_UserRoles
UNION
SELECT NEWID() AS guid, dbo.Portal_UserGroups.UserId,
dbo.Portal_GroupRoles.RoleId, dbo.Portal_UserGroups.ClubId,
dbo.Portal_GroupRoles.GroupId AS GroupRole
FROM dbo.Portal_GroupRoles
INNER JOIN dbo.Portal_UserGroups
ON dbo.Portal_GroupRoles.GroupId = dbo.Portal_UserGroups.GroupId
) AS derivedtbl_1
WHERE EXISTS
(
SELECT Portal_GroupRoles.RoleId, Portal_ClubGroups.ClubId
FROM (Portal_GroupRoles
INNER JOIN Portal_ClubGroups
ON Portal_GroupRoles.GroupId = Portal_ClubGroups.GroupId) AS cgr
WHERE derivedtbl_1.RoleID = cgr.RoleId
AND derivedtbl_1.ClubId = cgr.ClubId
)
Be wary of splitting the two-column condition into two separate IN clauses; it does not give you the same answer (in general) as the applying the two-column condition in one EXISTS clause.
Do a join on the derived table instead of using the in
SELECT *
FROM
(
SELECT NEWID() AS guid, UserID, RoleId, ClubId, 0 AS GroupRole
FROM dbo.Portal_UserRoles
UNION
SELECT NEWID() AS guid, dbo.Portal_UserGroups.UserId,
dbo.Portal_GroupRoles.RoleId, dbo.Portal_UserGroups.ClubId,
dbo.Portal_GroupRoles.GroupId AS GroupRole
FROM dbo.Portal_GroupRoles
INNER JOIN dbo.Portal_UserGroups
ON dbo.Portal_GroupRoles.GroupId = dbo.Portal_UserGroups.GroupId
) AS derivedtbl_1
INNER JOIN
(
SELECT Portal_GroupRoles.RoleId, Portal_ClubGroups.ClubId
FROM Portal_GroupRoles
INNER JOIN Portal_ClubGroups
ON Portal_GroupRoles.GroupId = Portal_ClubGroups.GroupId
) derivedtbl_2
ON derivedtbl_1.RoleID = derivedtbl_2.RoleID
AND derivedtbl_1.ClubId = derivedtbl_2.ClubId
SELECT
/*
your selected fields, joins here
*/
WHERE -- (derivedtbl_1.RoleId,derivedtbl_1.ClubId) IN
EXISTS
(
-- actually you can change these two fields to * (asterisk ) or 1, whatever, even your name, what matters only is the testing of existence(see below)
SELECT Portal_GroupRoles.RoleId, Portal_ClubGroups.ClubId
FROM Portal_GroupRoles
INNER JOIN Portal_ClubGroups
ON Portal_GroupRoles.GroupId = Portal_ClubGroups.GroupId
-- here is your IN (the testing of existence):
WHERE Portal_GroupRoles.RoleId = derivedtbl_1.RoleId AND
AND derivedtbl_1.ClubId = derivedtbl_1.ClubId
)
alternatively:
SELECT
/*
your selected fields, joins here
*/
JOIN
(
SELECT Portal_GroupRoles.RoleId, Portal_ClubGroups.ClubId
FROM Portal_GroupRoles
INNER JOIN Portal_ClubGroups
ON Portal_GroupRoles.GroupId = Portal_ClubGroups.GroupId
) X
-- here is your IN:
ON X.RoleId = derivedtbl_1.RoleId
AND X.ClubId = derivedtbl_1.ClubId
Yours is useful syntax and is supported in standard SQL. It is not yet implemented in SQL Server but you may vote for its inclusion here.
You have to separate in two clauses
where col1 in (...) AND col2 in (...)
or you could refactor it a little bit
select * FROM (
SELECT NEWID() AS guid, UserID, RoleId, ClubId, 0 AS GroupRole FROM dbo.Portal_UserRoles
UNION
SELECT NEWID() AS guid, dbo.Portal_UserGroups.UserId, dbo.Portal_GroupRoles.RoleId, dbo.Portal_UserGroups.ClubId, dbo.Portal_GroupRoles.GroupId AS GroupRoles FROM dbo.Portal_GroupRoles INNER JOIN dbo.Portal_UserGroups ON dbo.Portal_GroupRoles.GroupId = dbo.Portal_UserGroups.GroupId)
AS derivedtbl_1, Portal_GroupRoles, Portal_ClubGroup
where derivedtbl_1.RoleId = Portal_GroupRoles.RoleId
and derivedtbl_1.ClubId = Portal_ClubGroups.ClubId
and Portal_GroupRoles.GroupId = Portal_ClubGroups.GroupId