Refactoring the a subquery within a select statement to a join - sql

I've got a table of products and a table of widgets.
A product is built from a number of widgets and I've got a linking table which shows which widgets are linked to which products.
Widgets can be marked as expired, which means they are not eligible to be added to any new products.
Schema and sample data in the following sqlFiddle -
I want a query that shows all the eligible widgets for a particular product given the following rules:
A widget should not be shown if it is marked as expired.
As an exception to the above rule - If a widget is marked as expired, but is already linked to a product, then it should be shown.
If a widget has already been linked to the product, it should be marked as 'selected'
I've got the following query which works, and satisfies all the above rules:
select
data.WidgetName,
data.expired,
case
when data.ID in (select data_id from Widget_link where productId = 1)
then 1
else 0
end as selected
from Widget_data data
left outer join Widget_link link
on data.ID = link.data_id and data.expired = 1
where (data.expired = 0 or (data.expired = 1 and link.productId = 1))
I'd like to find a way to refactor the sub-query within the select part of the query to some kind of join. I'm trying to create a view which I can filter using just a where clause, rather than having the productId in two places. Is this possible?

On simplification your query can solely be evaluated based on JOIN like below, and there is no need for inner query at all.
select data.WidgetName,
case when link.id is not null then 1 else 0 end as selected
from Widget_data data
left outer join Widget_link link
on data.ID = link.data_id and link.productId = 1
where data.expired=0 or link.id is not null
Demo SQL fiddle

You can use SQL CTE (Common Table Expression) to cover the results from a SELECT list, and use in a single SQL statement
You can even create SQL statements with multiple CTE's in your code
What I suggest is can be summarized visually as follows
WITH cte AS (
SELECT ..... FROM .....
),
other_cte AS (
SELECT ..... FROM .....
)
SELECT *
FROM cte
INNER JOIN other_cte ON...

Related

Query with Left outer join and group by returning duplicates

To begin with, I have a table in my db that is fed with SalesForce info. When I run this example query it returns 2 rows:
select * from SalesForce_INT_Account__c where ID_SAP_BAYER__c = '3783513'
When I run this next query on the same table I obtain one of the rows, which is what I need:
SELECT MAX(ID_SAP_BAYER__c) FROM SalesForce_INT_Account__c where ID_SAP_BAYER__c = '3783513' GROUP BY ID_SAP_BAYER__c
Now, I have another table (PedidosEspecialesZarateCabeceras) which has a field (NroClienteDireccionEntrega) that I can match with the field I've been using in the SalesForce table (ID_SAP_BAYER__c). This table has a key that consists of just 1 field (NroPedido).
What I need to do is join these 2 tables to obtain a row from PedidosEspecialesZarateCabeceras with additional fields coming from the SalesForce table, and in case those additional fields are not available, they should come as NULL values, so for that im using a LEFT OUTER JOIN.
The problem is, since I have to match NroClienteDireccionEntrega and ID_SAP_BAYER__c and there's 2 rows in the salesforce table with the same ID_SAP_BAYER__c, my query returns 2 duplicate rows from PedidosEspecialesZarateCabeceras (They both have the same NroPedido).
This is an example query that returns duplicates:
SELECT
cab.CUIT AS CUIT,
convert(nvarchar(4000), cab.NroPedido) AS NroPedido,
sales.BillingCity__c as Localidad,
sales.BillingState__c as IdProvincia,
sales.BillingState__c_Desc as Provincia,
sales.BillingStreet__c as Calle,
sales.Billing_Department__c as Distrito,
sales.Name as RazonSocial,
cab.NroCliente as ClienteId
FROM PedidosEspecialesZarateCabeceras AS cab WITH (NOLOCK)
LEFT OUTER JOIN
SalesForce_INT_Account__c AS sales WITH (NOLOCK) ON
cab.NroClienteDireccionEntrega = sales.ID_SAP_BAYER__c
and sales.ID_SAP_BAYER__c in
( SELECT MAX(ID_SAP_BAYER__c)
FROM SalesForce_INT_Account__c
GROUP BY ID_SAP_BAYER__c
)
WHERE cab.NroPedido ='5320'
Even though the join has MAX and Group By, this returns 2 duplicate rows with different SalesForce information (Because of the 2 salesforce rows with the same ID_SAP_BAYER__c), which should not be possible.
What I need is for the left outer join in my query to pick only ONE of the salesforce rows to prevent duplication like its happening right now. For some reason the select max with the group by is not working.
Maybe I should try to join this tables in a different way, can anyone give me some other ideas on how to join the two tables to return just 1 row? It doesnt matter if the SalesForce row that gets picked out of the 2 isn't the correct one, I just need it to pick one of them.
Your IN clause is not actually doing anything, since...
SELECT MAX(ID_SAP_BAYER__c)
FROM SalesForce_INT_Account__c
GROUP BY ID_SAP_BAYER__c
... returns all possible IDSAP_BAYER__c values. (The GROUP BY says you want to return one row per unique ID_SAP_BAYER__c and then, since your MAX is operating on exactly one unique value per group, you simply return that value.)
You will want to change your query to operate on a value that is actually different between the two rows you are trying to differentiate (probably the MAX(ID) for the relevant ID_SAP_BAYER__c). Plus, you will want to link that inner query to your outer query.
You could probably do something like:
...
LEFT OUTER JOIN
SalesForce_INT_Account__c sales
ON cab.NroClienteDireccionEntrega = sales.ID_SAP_BAYER__c
and sales.ID in
(
SELECT MAX(ID)
FROM SalesForce_INT_Account__c sales2
WHERE sales2.ID_SAP_BAYER__c = cab.NroClienteDireccionEntrega
)
WHERE cab.NroPedido ='5320'
By using sales.ID in ... SELECT MAX(ID) ... instead of sales.ID_SAP_BAYER__c in ... SELECT MAX(ID_SAP_BAYER__c) ... this ensures you only match one of the two rows for that ID_SAP_BAYER__c. The WHERE sales2.ID_SAP_BAYER__c = cab.NroClienteDireccionEntrega condition links the inner query to the outer query.
There are multiple ways of doing the above, especially if you don't care which of the relevant rows you match on. You can use the above as a starting point and make it match your preferred style.
An alternative might be to use OUTER APPLY with TOP 1. Something like:
SELECT
...
FROM PedidosEspecialesZarateCabeceras AS cab
OUTER APPLY(
SELECT TOP 1 *
FROM SalesForce_INT_Account__c s1
WHERE cab.NroClienteDireccionEntrega = s1.ID_SAP_BAYER__c
) sales
WHERE cab.NroPedido ='5320'
Without an ORDER BY the match that TOP 1 chooses will be arbitrary, but I think that's what you want anyway. (If not, you could add an ORDER BY).

TSQL - Alternative resultset when IN clause returns empty

I need help to implement a filter within a bigger CTE query.
Imagine this scenario:
List of items that may belong to several categories.
User follows one of those categories.
Filter: user MAY specify that when one category is followed he/she only wants to view items that also belong to at least one of the auxiliary categories he/she can specify.
Example: user follows the category 'Computers' and specified that only wants to view 'Tablets'. When the user views 'Computers', the query should only select items that also belong to the 'Tablets' category. When there are no filters specified, all items of the main category ('Computers') should be presented.
SQL Fiddle seems to have some problem at the moment, so here's a little schema and query (at the end) that illustrate the problem
SELECT * FROM ItemsToShowWithCategories itswh_cte1
WHERE /* Other filters AND */
itswh_cte1.ItemPk IN (
SELECT DISTINCT itswh_cte2.ItemPk
FROM ItemsToShowWithCategories AS itswh_cte2
INNER JOIN ItemsToShowWithCategories AS itswh3_cte
ON itswh_cte2.ItemPk=itswh3_cte.ItemPk
LEFT JOIN CategoriesRequiredByCategory_CTE AS crbc_cte
ON itswh_cte2.FkCategory=crbc_cte.FkCategoryView
AND crbc_cte.FkCategoryRequired=itswh3_cte.FkCategory
WHERE crbc_cte.FkCategoryView IS NOT NULL
);
Question: how can I return all the records when the IN clause returns empty?
Well, the most obvious would be something like this:
WHERE /* Other filters AND */
itswh_cte1.ItemPk IN (
SELECT itswh_cte2.ItemPk
FROM ItemsToShowWithCategories AS itswh_cte2
INNER JOIN ItemsToShowWithCategories AS itswh3_cte
ON itswh_cte2.ItemPk=itswh3_cte.ItemPk
LEFT JOIN CategoriesRequiredByCategory_CTE AS crbc_cte
ON itswh_cte2.FkCategory=crbc_cte.FkCategoryView
AND crbc_cte.FkCategoryRequired=itswh3_cte.FkCategory
WHERE crbc_cte.FkCategoryView IS NOT NULL
)
or
(SELECT count(ItemPk)
FROM ItemsToShowWithCategories AS itswh_cte2
INNER JOIN ItemsToShowWithCategories AS itswh3_cte
ON itswh_cte2.ItemPk=itswh3_cte.ItemPk
LEFT JOIN CategoriesRequiredByCategory_CTE AS crbc_cte
ON itswh_cte2.FkCategory=crbc_cte.FkCategoryView
AND crbc_cte.FkCategoryRequired=itswh3_cte.FkCategory
WHERE crbc_cte.FkCategoryView IS NOT NULL) = 0
There's a few variants you could try if this performs poorly, for example using (select top 1 ItemPk ...) is null or something like that. A CTE will allow you to avoid writing the same SQL twice (useful if this isn't generated SQL).

Query to find sub departments vb.net mssacces 97 sql

I am trying to find subdepartments where they link by description if one is their it should return the id of the one in the table I am using the following query.
SELECT DISTINCT Subdeptcode
,SubDepartment
FROM SkechersPricat
WHERE SubDepartment IN ( SELECT DISTINCT description
FROM SubDept )
AND processed = false
AND SubDeptCode = 0
I need To be able to return the subdeptcode from the sub dept table if one exists if one Doesnt exist create it so I need a query that can account for both condision im using vb.net
Main File looks like the following
Sub Debt table above
For starters lets look at your query
SELECT DISTINCT Subdeptcode
,SubDepartment
FROM SkechersPricat
WHERE SubDepartment IN ( SELECT DISTINCT description
FROM SubDept )
AND processed = false
AND SubDeptCode = 0
you are trying to say that SubDepartment column needs to be in one of the values of description from SubDept table. Unless your SubDepartment matches description exactly it will not return anything.
Proper syntax for your query would be using LEFT OUTER JOIN. By using LEFT OUTER JOIN you are saying to get all results from your left table and only records that match in your right table. Thus you still get all the records from main table even if there is no match in SubDept table.
Your query will be like this
SELECT DISTINCT s.Subdeptcode
,s.SubDepartment
FROM SkechersPricat s
LEFT OUTER JOIN SubDept sd
ON s.SubDepartment = sd.description
WHERE s.processed = false
AND s.SubDeptCode = 0
What I do see in your screenshot that GeminDepartmentID corresponds to DeptCode from SubDept table. I think you are not joining on correct value.

Referring to a column from a different table in inner join select

I'm trying to join onto an inner query, which is having it's results filtered by a value in the other table, so I can select the top result and use a value in it multiple times in my main select statement, but I get the error as below:
The multi-part identifier "TessSCCall.Call_Num" could not be bound.
See below for the code so far:
SELECT BestAppointmentOffer AS foo -- I'm using this lots of times in different fields
BestAppointmentOffer AS bar -- I'm using this lots of times in different fields
....
FROM TessSCEmploy
INNER JOIN TessSCCall on TessSCEmploy.Employ_Num = TessSCCall.Call_Employ_Num
INNER JOIN
(
SELECT TOP 1 dbo.Aqua_Midnight(AppointmentStartTime)
AS BestAppointmentOffer, CallNumber
FROM AQWEB.[360Tracking].dbo.AppointmentOffers
WHERE CallNumber = TessSCCall.Call_Num
ORDER BY AppointmentStartTime) AS Appointment
on TessSCCall.Call_Num = Appointment.CallNumber
where ....
How can I get this to work, so I can use the value from my query (that I'm currently trying to join) in calculations in multiple fields, without repeating it?
The easiest way to do this would be to use CROSS APPLY but since you are using SQL Server 2000 you don't have that option. You should be able to use an aggregate function to get the top result for each appointment time:
select BestAppointmentOffer AS foo -- I'm using this lots of times in different fields
BestAppointmentOffer AS bar -- I'm using this lots of times in different fields
....
from TessSCEmploy
inner join TessSCCall
on TessSCEmploy.Employ_Num = TessSCCall.Call_Employ_Num
INNER JOIN
(
SELECT min(dbo.Aqua_Midnight(AppointmentStartTime)) AS BestAppointmentOffer,
CallNumber
FROM AQWEB.[360Tracking].dbo.AppointmentOffers
GROUP BY CallNumber
) AS Appointment
on TessSCCall.Call_Num = Appointment.CallNumber
where ....
You need to CROSS APPLY to correlate columns in a derived table
...
TessSCEmploy inner join TessSCCall on TessSCEmploy.Employ_Num = TessSCCall.Call_Employ_Num
CROSS APPLY
(SELECT TOP 1 dbo.Aqua_Midnight(AppointmentStartTime) AS BestAppointmentOffer, CallNumber
FROM AQWEB.[360Tracking].dbo.AppointmentOffers
WHERE CallNumber = TessSCCall.Call_Num
ORDER BY AppointmentStartTime) AS Appointment on TessSCCall.Call_Num = Appointment.CallNumber
where ....
CROSS APPLY is the correct construct anyway for what you are doing, which is a "TOP 1 per Something"

How do I write an SQL query to identify duplicate values in a specific field?

This is the table I'm working with:
I would like to identify only the ReviewIDs that have duplicate deduction IDs for different parameters.
For example, in the image above, ReviewID 114 has two different parameter IDs, but both records have the same deduction ID.
For my purposes, this record (ReviewID 114) has an error. There should not be two or more unique parameter IDs that have the same deduction ID for a single ReviewID.
I would like write a query to identify these types of records, but my SQL skills aren't there yet. Help?
Thanks!
Update 1: I'm using TSQL (SQL Server 2008) if that helps
Update 2: The output that I'm looking for would be the same as the image above, minus any records that do not match the criteria I've described.
Cheers!
SELECT * FROM table t1 INNER JOIN (
SELECT review_id, deduction_id FROM table
GROUP BY review_id, deduction_id
HAVING COUNT(parameter_id) > 1
) t2 ON t1.review_id = t2.review_id AND t1.deduction_id = t2.deduction_id;
http://www.sqlfiddle.com/#!3/d858f/3
If it is possible to have exact duplicates and that is ok, you can modify the HAVING clause to COUNT(DISTINCT parameter_id).
Select ReviewID, deduction_ID from Table
Group By ReviewID, deduction_ID
Having count(ReviewID) > 1
http://www.sqlfiddle.com/#!3/6e113/3 has an example
If I understand the criteria: For each combination of ReviewID and deduction_id you can have only one parameter_id and you want a query that produces a result without the ReviewIDs that break those rules (rather than identifying those rows that do). This will do that:
;WITH review_errors AS (
SELECT ReviewID
FROM test
GROUP BY ReviewID,deduction_ID
HAVING COUNT(DISTINCT parameter_id) > 1
)
SELECT t.*
FROM test t
LEFT JOIN review_errors r
ON t.ReviewID = r.ReviewID
WHERE r.ReviewID IS NULL
To explain: review_errors is a common table expression (think of it as a named sub-query that doesn't clutter up the main query). It selects the ReviewIDs that break the criteria. When you left join on it, it selects all rows from the left table regardless of whether they match the right table and only the rows from the right table that match the left table. Rows that do not match will have nulls in the columns for the right-hand table. By specifying WHERE r.ReviewID IS NULL you eliminate the rows from the left hand table that match the right hand table.
SQL Fiddle