I have a requirement to pick up data that is in more than one place and I have some form of recognition if using the coalesce function. Basically I am looking to coalesce the join itself but looking online its seems as if i can only do this on the fields.
So we have a Products and Suppliers table, we also have these as a temp table so in total 4 tables (products, tempproducts, suppliers, tempsuppliers). In the suppliers and products table is where we store our products and suppliers and their temptables we store any new suppliers/products. We also have a tempsupplierproduct which joins new suppliers to new products. However we can end in a situation where a new supplier has an existing product so the new supplier will be in the tempsuppliers table and its product is in the products table NOT the tempproducts as it is not new, we will also have a new tempsupplierproduct to join the two up.
So i want a query which looks in the tempsupplierproducts table and then gets basic information about the supplier and products. To do this i am using a coalesce.
SELECT DISTINCT SP.*, COALESCE(P.Product, PD.Product) 'Product', COALESCE(S.Supplier, SU.Supplier) 'Supplier'
FROM tempsupplierproduct SP
LEFT JOIN tempProduct P ON SP.ProductCode = P.Code
LEFT JOIN Products PD ON SP.ProductCode = PD.Code
LEFT JOIN tempSupplier S ON SP.SupplierCode = S.Code
LEFT JOIN Suppliers SU ON SP.SupplierCode = SU.Code
Now while this works, something at the back of my head tells me it is not entirely right, ideally i want if data is not in table A then join to table B. I have seen maybe coalescing inside the join itself but I am unsure how to do this
LEFT JOIN Suppliers Su ON SP.SupplierCode = COALESCE(S.Code, SU.Code)
maybe away, but I am confused by this, all it is saying is use code in temptable if not there then use supplier code. So what would this mean if we have a code in the temptable, will this try to join on it, if so then this is incorrect also.
Any help is appreciated
You can union the two suppliers tables together and then join them in one go like this. I'm assuming that there are no duplicates between the two tables in this case but with a bit of extra work that could be resolved as well.
WITH AllSuppliers AS
(
SELECT Code, Supplier FROM Suppliers
UNION ALL
SELECT Code, Supplier FROM tempSupplier
)
SELECT DISTINCT SP.*, COALESCE(P.Product, PD.Product) 'Product', S.Supplier
FROM tempsupplierproduct SP
LEFT JOIN tempProduct P ON SP.ProductCode = P.Code
LEFT JOIN Products PD ON SP.ProductCode = PD.Code
LEFT JOIN AllSuppliers S ON SP.SupplierCode = S.Code
If you need to handle duplicates in the two suppliers tables then an approach like this should work, essentially we rank the duplicates and then pick the highest ranked result. For two tables you could use a full outer join between the two but this approach will scale to any number of tables.
WITH AllSuppliers AS
(
SELECT Code, Supplier, 1 AS TablePriority FROM Suppliers
UNION ALL
SELECT Code, Supplier, 2 AS TablePriority FROM tempSupplier
),
SuppliersRanked AS
(
SELECT Code, Supplier,
ROW_NUMBER() OVER (PARTITION BY Code ORDER BY TablePriority) AS RowPriority
FROM AllSuppliers
)
SELECT DISTINCT SP.*, COALESCE(P.Product, PD.Product) 'Product', S.Supplier
FROM tempsupplierproduct SP
LEFT JOIN tempProduct P ON SP.ProductCode = P.Code
LEFT JOIN Products PD ON SP.ProductCode = PD.Code
LEFT JOIN SuppliersRanked S ON SP.SupplierCode = S.Code
AND RowPriority = 1
You can absolutely join on a coalesced field. Here is a snippet from one of my production views:
LEFT JOIN [Portal].tblHelpdeskresource supplier ON PO.fld_str_SupplierID = supplier.fld_str_SupplierID
-- Job type a
LEFT JOIN [Portal].tblHelpDeskFault HDF ON PO.fld_int_HelpdeskFaultID = HDF.fld_int_ID
-- Job Type b
LEFT JOIN [Portal].tblProjectHeader PH ON PO.fld_int_ProjectHeaderID = PH.fld_int_ID
LEFT JOIN [Portal].tblPPMScheduleLine PSL ON PH.fld_int_PPMScheduleRef = PSL.fld_int_ID
-- Managers (used to be separate for a & b type, now converged)
LEFT JOIN [Portal].uvw_HelpDeskSiteManagers PSM ON COALESCE(PSL.fld_int_StoreID,HDF.fld_int_StoreID) = PSM.PortalSiteId
LEFT JOIN [Portal].tblHelpdeskResource PHDR ON PSM.PortalResourceId = PHDR.fld_int_ID
Related
I have a bit of a complicated scenario. I have two tables, employee and agency. An employee may or may not have an agency, but if an employee has an agency I want the select clause to check another condition on the agency, but if the employee does not have an agency its fine I want to fetch the employee. I'm not sure how to write the select statement for this. This is what I have come up with so far
select * from employee e left join
agency a on a.id = e.agencyID and a.valid = true;
However the problem with this is that it fetches both employees without agencies which is fine, but it also fetches employees with agencies where a.valid = false. The only option I can think of is to do an union but I'm looking for something more simpler.
A UNION could actually be the solution that performs best, but you can write the query without UNION like this:
select *
from employee e
left join agency a
on a.id = e.agencyID
where coalesce(a.valid, true);
That will accept agencies where valid IS NULL, that is, result rows where the agency part was substituted with NULLs by the outer join.
You want except the condition that both table match(agency.id = employee.agencyID) and also agency.id is false. The following query will express the condition.
SELECT
e.*,
a.*
FROM
employee e
LEFT JOIN agency a ON a.id = e.agencyID
WHERE
NOT EXISTS (
SELECT
1
FROM
agency
WHERE
a.id = e.agencyID
AND a.valid IS FALSE)
ORDER BY
e.id;
I have the following schema
I wanted to get all cars along with number of models for each car and number of remaining colors for each car.
I was able to get number of models but i am not able to get number of remaining colors for each car. I know i have to do Left [Outer] Join without Intersection. But its not working
I may also have model which does not have any colors. In such case there wont be any entry in ModelColors table
select
c.CarID,
c.CarName,
T1.[Num Of Models],
T2.[Remaining Colors]
from Cars c
LEFT JOIN
(
SELECT m.CarID, COUNT(1) AS 'Num Of Models'
FROM Models m
GROUP BY m.CarID
) AS T1 ON T1.CarID = c.CarID
LEFT JOIN
(
SELECT m1.CarID, COUNT(1) AS 'Remaining Colors'
FROM Colors col
LEFT JOIN ModelColors mc on mc.ColorID = col.ColorID
LEFT JOIN Models m1 on m1.ModelID = mc.ModelID
WHERE mc.ColorID IS NULL
GROUP BY m1.CarID
) AS T2 ON T2.CarID = c.CarID
Your from/join clause in the second derived table (T2) is wrong.
You should use Models and ModelColors only:
SELECT m1.CarID, COUNT(1) AS 'Remaining Colors'
FROM Models m
LEFT JOIN ModelColors mc
ON m.ModelID = mc.ModelID
The entire query should look like this:
SELECT
c.CarID,
c.CarName,
T1.[Num Of Models],
T2.[Remaining Colors]
FROM Cars c
LEFT JOIN
(
SELECT m.CarID, COUNT(1) AS 'Num Of Models'
FROM Models m
GROUP BY m.CarID
) AS T1 ON T1.CarID = c.CarID
LEFT JOIN
(
SELECT m1.CarID, COUNT(1) AS 'Remaining Colors'
FROM Models m
LEFT JOIN ModelColors mc
ON m.ModelID = mc.ModelID
) AS T2 ON T2.CarID = c.CarID
Since you only want to count the colors, you don't need the Colors table at all for this query.
Select p.prodCode,
p.description,
p.unit,
SUM(sd.quantity) "Total quantity"
FROM salesDetail sd
RIGHT JOIN product p
ON p.prodCode = sd.prodCode
GROUP BY p.prodCode
ORDER BY 4 DESC
Help! My Script is not running. I need to get the total quantity of every product but my group by is not working.
Compute the sum of the quantity per product in separate subquery, and then join this back to the original product table:
SELECT t1.prodCode,
t1.description,
t1.unit,
t2.total_quantity
FROM product t1
INNER JOIN
(
SELECT p.prodCode, SUM(sd.quantity) total_quantity
FROM product p
LEFT JOIN salesDetail sd
ON p.prodCode = sd.prodCode
GROUP BY p.prodCode
) t2
ON t1.prodCode = t2.prodCode
Note that I replaced the RIGHT JOIN with a LEFT JOIN by switching the order of the joined tables in the subquery.
Update:
If you absolutely need to use a RIGHT JOIN, then just replace the subquery with this:
SELECT p.prodCode, SUM(sd.quantity) total_quantity
FROM salesDetail sd
RIGHT JOIN product p
ON p.prodCode = sd.prodCode
GROUP BY p.prodCode
I manage to do the selection with more selects and a loop. 4 tables ( the last one was just for collecting all the data )
But now i'm thinking of a way to select all the fields i need with just one select statement. Here is the huge select :)
SELECT vbak~vbeln vbak~audat
tvakt~bezei
vbap~posnr vbap~matnr vbap~kwmeng vbap~vrkme
lips~vbeln lips~posnr lips~werks lips~lfimg
vbfa~vbtyp_n
FROM vbak JOIN vbap ON vbak~vbeln = vbap~vbeln
JOIN tvakt ON vbak~auart = tvakt~auart
LEFT JOIN vbfa ON vbfa~vbelv = vbak~vbeln AND vbfa~posnv = vbap~posnr
JOIN lips ON vbfa~vbeln = lips~vbeln AND vbfa~posnn = lips~posnr
INTO TABLE gt_salord
WHERE tvakt~spras = 'EN' AND
vbak~vbeln IN s_vbeln AND
vbak~audat IN s_audat.
The problem is this doesn't work. When i try to activate it throws this error: " Unable to compare with "VBAP~POSNR". A table can be joined with a maximum of one other table using LEFT OUTER JOIN "
If i don't use LEFT JOIN and only JOIN it works but i don't get all what i want. I need to get all the SALES ORDERS even if they don't have a DELIVERY ORDER assigned. Is there a way to do that, or do i really have to split my select?
I have noticed in SAP that it's more efficient to simplify select statements and proceed with LOOP and SELECT SINGLE for table that do not participate in data selection.
In your case data from table VBFA could be fetch after data selection (it is not restricting the amount of data fetched from the DB).
Of course it depends on indexes, application server buffering... but, even though it might be counter-intuitive for SQL experts, keeping select statements not too complex in SAP is best.
Can you try the following selection:
SELECT vbak~vbeln vbak~audat
tvakt~bezei
vbap~posnr vbap~matnr vbap~kwmeng vbap~vrkme
lips~vbeln lips~posnr lips~werks lips~lfimg
vbfa~vbtyp_n
FROM vbak JOIN vbap ON vbak~vbeln = vbap~vbeln
JOIN tvakt ON vbak~auart = tvakt~auart
LEFT JOIN vbfa ON vbfa~vbelv = vbap~vbeln AND vbfa~posnv = vbap~posnr
JOIN lips ON vbfa~vbeln = lips~vbeln AND vbfa~posnn = lips~posnr
INTO TABLE gt_salord
WHERE tvakt~spras = 'EN' AND
vbak~vbeln IN s_vbeln AND
vbak~audat IN s_audat.
I can't test the result, but the syntax check say: ok.
There is only one tiny difference:
x---- difference
v
LEFT JOIN vbfa ON vbfa~vbelv = vbap~vbeln AND vbfa~posnv = vbap~posnr
LEFT JOIN vbfa ON vbfa~vbelv = vbak~vbeln AND vbfa~posnv = vbap~posnr
You compared vbfa~vbelv with vbak~vbeln, I do it with vbap~vbeln. Both have the same value, but in the on-clause you use again vbap.
I dont know about SAP Abap . But from SQL point of view you can use derived query if it is supported in SAP.
here is some approach :
select * from
(
select * from
table1 inner join table2 on table1.key=table2.key
inner join table3 on table1.key=table3.key
) a left outer join table4 b
on a.key=b.key
Posting this as the question is tagged as SQL. Hope it works
Try to change the order of table fields in the on clause of left join. Put vbap~vbeln = vbfa~vbelv
I have a stored procedure which is running quite slow. Therefore I want to extract some of the query in a separate view.
My code looks something like this:
DECLARE #tmpTable TABLE(..)
INSERT INTO #tmpTable (..) *query* (returns 3000 rows)
Select ... from table1
inner join table2
inner join table3
inner join #tmpTable
...
I then extract (copy-paste) the *query* and put it in a view - i.e. vView.
Doing this will then give me a different result:
Select ... from table1
inner join table2
inner join table3
inner join vView
...
Why? I can see that the vView and the #tmpTable both returns 3000 rows, so they should match (also did a except query to check).
Any comments would be much appriciated as I feel quite stuck with this..
EDITED:
This is the full query for getting the result (using #tmpTable or vView gives me different results, although the appear the same):
select dep.sid as depsid, dep.[name], COUNT(b.sid) as possiblelogins, count(ls.clientsid) as logins
from department dep
inner join relationship r on dep.sid=r.primarysid and r.relationshiptypeid=27 and r.validto is null
inner join [user] u on r.secondarysid=u.sid
inner join relationship r2 on u.sid=r2.secondarysid and r2.validto is null and r2.relationshiptypeid in (1,37)
inner join client c on r2.primarysid=c.sid
inner join ***#tmpTable or vView*** b on b.sid = c.sid
left outer join (select distinct clientsid from logonstatistics) as ls on b.sid=ls.clientsid
GROUP BY dep.sid, dep.[name],dep.isdepartment
HAVING dep.isdepartment=1
You maybe don't need the view/table if you change to this.
It joins on to client c and appears to be there only to JOIN onto logonstatistics
--remove inner join ***#tmpTable or vView*** b on b.sid = c.sid
--change JOIN
left outer join (select distinct clientsid from logonstatistics) as ls on c.sid=ls.clientsid
And change COUNT(b.sid) to COUNT(c.sid) in the SELECT clause
Otherwise, if you get different results you have two options I can see:
Table and view have different data. Have you run a line by line comparsion?
One has NULL, one has a value (especially for the sid column which will affect the JOIN)
Finally, when you says "different results" do you mean you get x2 or x3 rows? A different COUNT? What?