SQL Server - tsql join/filtering issue - sql

Probably the wrong title, but I can't summarise what I'm trying to do nicely. Which is probably why my googling hasn't helped.
I have a list of Discounts, and a list of TeamExclusiveDiscounts (DiscountId, TeamId)
I call a stored procedure passing in #TeamID (int).
What I want is all Discounts except if they're in TeamExclusiveDiscounts and don't have TeamID matching #TeamId.
So the data is something like
Table Discount:
DiscountID Name
-----------------------
1 Test 1
2 Test 2
3 Test 3
4 Test 4
5 Test 5
Table TeamExclusiveDiscount:
DiscountID TeamID
-----------------------
1 10
2 10
2 4
3 8
Expected results:
searching for TeamID = 10 I should get discounts 1,2,4,5
searching for TeamID = 5 I should get discounts 4, 5
searching for TeamID = 8 I should get discounts 3, 4, 5
I've tried a variety of joins, or trying to update a temp table to set whether the discount is allowed or not, but I just can't seem to get my head around this issue.
So I'm after the T-SQL for my stored procedure that will select the correct discounts (SQL Server). Thanks!

SELECT D.DiscountID FROM Discounts D
LEFT JOIN TeamExclusiveDiscount T
ON D.DiscountID=T.DiscountID
WHERE T.TeamID=#TeamID OR T.TeamID IS NULL
SQLFIDDLE for TEST

Can you try this - it only selects records where there is a teamdiscount record with the team or no teamdiscount record at all.
SELECT * FROM Discounts D
WHERE
EXISTS (
SELECT 1
FROM TeamExclusiveDiscount T
WHERE T.DiscountID = D.DiscountID
AND TeamID = #TeamID
)
OR
NOT EXISTS (
SELECT 1
FROM TeamExclusiveDiscount T
WHERE T.DiscountID = D.DiscountID
)

I like to translate the English description directly into SQL (atleast as a first pass):
"All Discounts except if they're in TeamExclusiveDiscounts and don't have TeamID matching #TeamId."
SELECT *
FROM Discounts D -- All Discounts
WHERE D.DiscountID NOT IN -- except if they're in TeamExclusiveDiscounts
(SELECT T.DiscountID
FROM TeamExclusiveDiscount T
WHERE T.DiscountID NOT IN -- and don't have TeamID matching #TeamId.
(SELECT Match.DiscountID
FROM TeamExclusiveDiscount Match
WHERE Match.TeamID = #TeamID)
)

Related

Select ID with specific values in more than one field

I have a table as follows
groupCode
ProductIdentifier
1
dental
1
membership
2
dental
2
vision
2
health
3
dental
3
vision
I need to find out if a specific groupCode have "dental", "vision" and "health" (all three simultaneously)
The expected result is code 2
What I need to identify is if groupCode 2 has the three products (or two, or whatever the user enters). This is part of a huge kitchen sink query I'm building.
I'm doing
SELECT groupCode
FROM dbo.table
WHERE (productIdentifier = N'dental')
AND (productIdentifier = N'vision')
AND (productIdentifier = N'health')
AND (groupCode = 2)
But clearly is wrong because it's not working.
I tried to do something like its described here but it didn't return a result for me:
Select rows with same id but different value in another column
Thanks.
If each of 'dental','vision' and 'health' occur only once per group identifier, you can group by group identifier and filter by the groups having count(*) = 3:
WITH
-- your input ..
indata(groupCode,ProductIdentifier) AS (
SELECT 1,'dental'
UNION ALL SELECT 1,'membership'
UNION ALL SELECT 2,'dental'
UNION ALL SELECT 2,'vision'
UNION ALL SELECT 2,'health'
UNION ALL SELECT 3,'dental'
UNION ALL SELECT 3,'vision'
)
-- real query starts here ...
SELECT
groupcode
FROM indata
WHERE productidentifier IN ('dental','vision','health')
GROUP BY
groupcode
HAVING COUNT(*) = 3;
-- out groupcode
-- out -----------
-- out 2
As per Marcothesane answer, if you know the groupCode (2) and the number of products (vision, dental and health), 3 in this case, and you need to confirm if that code has those three specific products, this will work for you:
SELECT COUNT(groupCode) AS totalRecords
FROM dbo.table
WHERE (groupCode = 2) AND (productIdentifier IN ('dental', 'vision', 'health'))
HAVING (COUNT(groupCode) = 3)
This will return 3 (number of records = number of products).
Its basically's Marcothesane answer in a way you can "copy/paste" to your code by just changing the table name. You should accept Marcothesane answer.

Where statement for exact match on Many to Many SQL tables

I am trying to construct a SQL statement to search in two tables that are in a many to many relation.
Problem : SQL statement to search for products with exact stones.
For example, in the below tables, I need a statement that will search for product with Ruby and Emerald stone ONLY. In all my attempts I get both Ring and Necklace because they both have Ruby and Emerald even though Necklace has one additional stone. It should only give Ring product.
I need a way to implement the AND operator on the stone table so that the result contains products that have the exact stones. Please help.
Table stone
s_id
s_name
1
Ruby
2
Emerald
3
Onyx
Table product
p_id
p_name
1
Ring
2
Necklace
3
Pendent
Relation table - product_stone
p_s_id
p_id
s_id
1
1
1
1
1
2
1
2
1
1
2
2
1
2
3
1
3
3
This is a relational division question. We need to find the cross join of the two tables "divided" by our list, with no remainder i.e. no other stone in product.
We will assume that p_id and s_id are unique:
;WITH StonesToFind AS ( -- we could also use a table variable etc here
SELECT *
FROM stone
WHERE s_name IN ('Ruby','Emerald')
)
SELECT p.p_name
FROM product AS p -- let's get all products...
JOIN product_stone AS ps ON ps.p_id = p.p_id -- ...cross join all their stones
LEFT JOIN StonesToFind AS s ON s.s_id = ps.s_id -- they may have stones in the list
GROUP BY p.p_id, p_name
HAVING COUNT(CASE WHEN s.s_id IS NULL THEN 1 END) = 0
-- the number of non matching stones in product must be zero
AND COUNT(*) = (SELECT COUNT(*) FROM StonesToFind);
-- the total number of stones must be the same as the list

SQL To delete number of items is less than required item number

I have two tables - StepModels (support plan) and FeedbackStepModels (feedback), StepModels keeps how many steps each support plan requires.
SELECT [SupportPlanID],COUNT(*)AS Steps
FROM [StepModels]
GROUP BY SupportPlanID
SupportPlanID (Steps)
-------------------------------
1 4
2 9
3 3
4 10
FeedbackStepModels keeps how many steps employee entered the system
SELECT [FeedbackID],SupportPlanID,Count(*)AS StepsNumber
FROM [FeedbackStepModels]
GROUP BY FeedbackID,SupportPlanID
FeedbackID SupportPlanID
---------------------------------------------
1 1 3 --> this suppose to be 4
2 2 9 --> Correct
3 3 0 --> this suppose to be 3
4 4 10 --> Correct
If submitted Feedback steps total is less then required total amount I want to delete this wrong entry from the database. Basically i need to delete FeedbackID 1 and 3.
I can load the data into List and compare and delete it, but want to know if we can we do this in SQL rather than C# code.
You can use the query below to remove your unwanted data by SQL Script
DELETE f
FROM FeedbackStepModels f
INNER JOIN (
SELECT [FeedbackID],SupportPlanID, Count(*) AS StepsNumber
FROM [FeedbackStepModels]
GROUP BY FeedbackID,SupportPlanID
) f_derived on f_derived_FeedbackID=f.FeedBackID and f_derived.SupportPlanID = f.SupportPlanID
INNER JOIN (
SELECT [SupportPlanID],COUNT(*)AS Steps
FROM [StepModels]
GROUP BY SupportPlanID
) s_derived on s_derived.SupportPlanID = f.SupportPlanID
WHERE f_derived.StepsNumber < s_derived.Steps
I think you want something like this.
DELETE FROM [FeedbackStepModels]
WHERE FeedbackID IN
(
SELECT a.FeedbackID
FROM
(
SELECT [FeedbackID],
SupportPlanID,
COUNT(*) AS StepsNumber
FROM [FeedbackStepModels]
GROUP BY FeedbackID,
SupportPlanID
) AS a
INNER JOIN
(
SELECT [SupportPlanID],
COUNT(*) AS Steps
FROM [StepModels]
GROUP BY SupportPlanID
) AS b ON a.SupportPlanID = b.[SupportPlanID]
WHERE a.StepsNumber < b.Steps
);

SQL Select to return one line multiple times based on a number within the dataset

So I've tried many other attempts at answers around this topic from here and so far everything has either outright failed or not given me the result I'm after:
I have a select statement to use for a report that brings through delivery information. The result set is from a main table that only has one line per delivery number (the delivery header record) and within the dataset there is also a field called palletspaces which we use to indicate (you guessed it) how many pallets are needed for the delivery
What I now need to do is the following:
find that palletspaces number
return the single delivery record the same number of times as that palletspaces number
include a new column in the results that counts up to that palletspaces number
so for instance, my SQL will return every record from the deliveries table and would look something like this
id traderid toaddressid county postcode palletspaces
D-124597 2101 2 READING RG6 1AZ 3
D-124600 20060 12 MAGOR, GWENT NP26 3DF 1
D-124601 20060 13 RUGBY CV23 8YH 2
So now, I'd need to see that palletspaces number, then return the particular line that many times and then also have a new column that counts these instances:
id traderid toaddressid county postcode palletspaces LineCount
D-124597 2101 2 READING RG6 1AZ 3 1
D-124597 2101 2 READING RG6 1AZ 3 2
D-124597 2101 2 READING RG6 1AZ 3 3
D-124600 20060 12 MAGOR, GWENT NP26 3DF 1 1
D-124601 20060 13 RUGBY CV23 8YH 2 1
D-124601 20060 13 RUGBY CV23 8YH 2 2
The other thing to mention is that naturally I'll have hundreds of different delivery records (all returned as one line each) and all will have differing palletspaces numbers. And of course stating the obvious I need the line to only replicate and count based on it's own palletspaces number
The SQL in use is as below
select
deliveries.id,
deliveries.traderid,
customers.name,
deliveries.toaddressid,
deliveries.eutransportid,
deliveries.street,
deliveries.city,
deliveries.county,
deliveries.postcode,
delivery_custom.palletspaces,
ectransport.ectranspdesc
from deliveries
INNER JOIN customers ON
deliveries.traderid = customers.id
INNER JOIN delivery_custom ON
deliveries.id = delivery_custom.id
INNER JOIN ectransport ON
deliveries.eutransportid = ectransport.ectranspcode
Try like this:
select deliveries.id,
deliveries.traderid,
customers.name,
deliveries.toaddressid,
deliveries.eutransportid,
deliveries.street,
deliveries.city,
deliveries.county,
deliveries.postcode,
delivery_custom.palletspaces,
ectransport.ectranspdesc
INTO #MyTemp
from deliveries
INNER JOIN customers ON
deliveries.traderid = customers.id
INNER JOIN delivery_custom ON
deliveries.id = delivery_custom.id
INNER JOIN ectransport ON
deliveries.eutransportid = ectransport.ectranspcode
;WITH CTE AS(
SELECT id,traderid,toaddressid,county,postcode,palletspaces,1 AS LineCount
FROM #MyTemp
UNION ALL
SELECT id,traderid,toaddressid,county,postcode,palletspaces,LineCount+1
FROM CTE
WHERE LineCount<palletspaces
)
SELECT *
FROM CTE
ORDER BY id, LineCount;
DROP TABLE #MyTemp
Hope this time you get it.
Using Recursive CTE, we can achieve this:
DECLARE #TAB TABLE ([D Number] VARCHAR(20) ,customer INT, postcode VARCHAR(20), palletspaces INT)
INSERT INTO #TAB VALUES('D-123456' ,19114, 'DA12 1TF' , 4)
INSERT INTO #TAB VALUES('D-111111' ,19114, 'DDDD 1TF' , 3)
;WITH CTE AS(
SELECT [D Number],customer,postcode,palletspaces,1 AS A
FROM #TAB
UNION ALL
SELECT [D Number],customer,postcode,palletspaces,A+1
FROM CTE
WHERE A<palletspaces
)
SELECT *
FROM CTE
ORDER BY [D Number], LineCount;
Output:
D Number customer postcode palletspaces LineCount
D-123456 19114 DA12 1TF 4 1
D-123456 19114 DA12 1TF 4 2
D-123456 19114 DA12 1TF 4 3
D-123456 19114 DA12 1TF 4 4

Select column by different column function in Access query

I have the following table in Access 2010.
EQID Breaker Circuit Rating
1 A One 1000
2 A Two 1500
3 A Three 500
4 A Four 1000
5 B One 1500
6 B Two 2000
I want to create a query to Group by Breaker, and show the Minimum Rating and the associated Circuit for that rating. I understand how to do this without showing the Circuit for the Minimum rating.
My desired query result would be:
EQID Breaker Circuit Rating
1 A Three 500
2 B One 1500
Try this:
SELECT a.*
FROM table AS a
INNER JOIN (
SELECT Breaker, MIN(Rating) AS min_rating
FROM table
GROUP BY Breaker
) AS b
ON a.Breaker = b.Breaker AND
a.Rating = b.min_rating;
SQLFiddle: http://www.sqlfiddle.com/#!2/ea4fb/2
You can try below:
SELECT t.EQID, t.Breaker, t.Circuit, t.Rating
FROM test t
INNER JOIN
(
SELECT a.Breaker, MIN(a.Rating) AS Rating
FROM test a
GROUP BY Breaker
) AS tmp
ON tmp.Breaker = t.Breaker AND tmp.Rating = t.Rating;
Sql fiddle demo: http://sqlfiddle.com/#!2/fe796/19