rewards the products qualify for - sql

products purchased
--------------------------
bana
bana
bana
stra
kiwi
reward requirements table (related to a rewards table)
reward id, products
----------------------
1,bana
1,bana
1,bana
2,stra
2,bana
3,stra
4,cart
5,bana
5,bana
5,oliv
Can you help me with sql to get rewards
the products purchased qualifies for?
In the case above the reward ids would be:
1
2
3
If there is a better design that would make the solution easier I welcome those as well. I'm using product names for the sake of easier explaining, I hope. (I'll replace with product ids later)

This query will solve your problem.
select r.reward_id
from (
select reward_id, product, count(*) needed
from reward_requirements
group by reward_id, product
) r
left join (
select product, count(*) bought
from products_purchased
group by product
) p on r.product=p.product and p.bought >= r.needed
group by r.reward_id
having count(reward_id) = count(distinct p.product)
order by r.reward_id
To make your design better, you could redo the reward_requirements to have the columns (product, needed) instead of having to list it multiple times. It will also get rid of the first subquery.

With this schema:
CREATE TABLE product
(
product_id int IDENTITY
, name varchar(50)
)
CREATE TABLE requirement
(
requirement_id int IDENTITY
, product_id int
, quantity int
, reward_id int
)
CREATE TABLE reward
(
reward_id int IDENTITY
, reward varchar(50)
)
CREATE TABLE purchased
(
purchased_id int IDENTITY
, product_id int
, quantity int
)
Your query becomes:
SELECT requirement.reward_id
FROM requirement
LEFT JOIN purchased
ON purchased.product_id = requirement.product_id
AND purchased.quantity >= requirement.quantity
GROUP BY requirement.reward_id
HAVING COUNT(purchased.product_id) = COUNT(requirement.reward_id);
Here's a SQLFiddle to play around with: http://sqlfiddle.com/#!3/e93c9/7

Related

Updating fields based on results of a query

So in this scenario a person can order products. I have a query that returns the result of an order ID and give the products that person ordered.
My issue is adapting this to an update query so the quantity from the OrderProduct table/query is then subtracted from the amount of stock in the Product Table.
SQL to create the table supplied below and the query to get the results
CREATE DATABASE StockControl;
CREATE TABLE Membership (
MemberID int NOT Null,
LastName varchar(255),
FirstName varchar(255),
Primary Key(MemberID)
);
CREATE TABLE Orders (
OrderID int NOT Null,
MemberID int,
Primary Key(OrderID)
);
CREATE TABLE Product (
ProductID int NOT Null,
Price int,
Stock int,
Primary Key(ProductID)
);
CREATE TABLE OrderProduct (
ProductID int,
OrderID int,
Quantity int
);
INSERT INTO Membership(MemberID,LastName,FirstName)
VALUES (2,'Me','Too'),(33,'Darren','Kelly');
INSERT INTO Orders(OrderID,MemberID)
VALUES (1,33),(5,2);
INSERT INTO Product(ProductID,Price,Stock)
VALUES (1,30,12),(2,25,12),(3,25,12),(4,25,12),(5,25,12),(6,25,12),(7,25,12),(8,25,12),(9,25,12),(10,25,12);
INSERT INTO OrderProduct(ProductID,OrderID,Quantity)
VALUES (1,5,1),(3,1,4),(9,1,6),(10,1,2);
This is the query to return all products as part of orderID 1, in the full version this will be able to be variable.
SELECT OrderProduct.ProductID, OrderProduct.OrderID, OrderProduct.Quantity, Product.Price, [Quantity]*[Price] AS Cost
FROM Product INNER JOIN OrderProduct ON Product.ProductID = OrderProduct.ProductID
GROUP BY OrderProduct.ProductID, OrderProduct.OrderID, OrderProduct.Quantity, Product.Price, [Quantity]*[Price]
HAVING (((OrderProduct.OrderID)=1))
ORDER BY OrderProduct.OrderID;
This returns the correct result but when trying to implement this to an update I get stuck. I want it so it looks through the 3 results and will subtract 4 from product 3, 6 from product 9 and 2 from product 10
Attempt so far
UPDATE SingleOrder INNER JOIN Product ON SingleOrder.ProductID = Product.ProductID SET Product.Stock = Product.Stock-SingleOrder.Quantity WHERE ((Product.ProductID=SingleOrder.ProductID));
If this is not clear I am happy to update the question etc.
Thanks

Returning values using a query that have been awarded a bonus

I'm trying to transactionId that were randomly chosen to be awarded a bonus which is essentially just having their rewardValue be double.
Following is my schema:
CREATE TABLE rewards (
rewardId INTEGER PRIMARY KEY,
rewardType VARCHAR(20),
rewardValue NUMERIC(6,2)
);
CREATE TABLE deposit (
depositId INTEGER PRIMARY KEY,
depositDate DATE,
customerId INTEGER NOT NULL REFERENCES Customers
);
CREATE TABLE transactions (
transactionId SERIAL PRIMARY KEY,
depositId INTEGER NOT NULL UNIQUE REFERENCES Orders,
transactionAmount NUMERIC(18,2)
);
Here's my query:
select distinct t.transactionId
from transactions t join deposit d on t.depositId = d.depositId join
rewards r on 2 * r.rewardValue <= t.transactionAmount;
I get some output which is just a few values repeating over and over. Does anyone know how to fix this?
before answer your question, seems like your join have a issue.
if you want to find transactions eligible for rewards
try this ->
select distinct transactionId from
(select t.transactionId,r.rewardValue,t.transactionAmount
from transactions t join deposit d on t.depositId = d.depositId,rewards r ) tbl where 2*rewardValue <= transactionAmount

Assign a product name based on the conditions given in SQL Server

I am trying to write a sql query based on the conditions below:
PRODUCT
ORDER
The Product Table has 3 columns based on the following query:
CREATE Product (MasterProduct nvarchar(50), Productkey int, ProductName nvarchar(50))
The Order Table has 3 columns based on the following query:
CREATE Order (MasterProduct nvarchar(50), Orderno int, Productkey int, ParentProduct int)
Scenario:
The 'MasterProduct' column in Product table should be assigned to a particular order in Order Table based on the following conditions:
1) If all the productkeys in product table for a specific Master Product match the order number in Order table
AND
The first ProductKey is a parent to its immediate product key in the Order Table
Example: The Order Number 'S1' is assigned a Kebab as Master Product for Order Table because product keys 1 and 2 from Product Table both exist in S1 and also product key 1 is a parent product key to product keys 2 and 4.
On the other hand, Order Number 'S2' isn't assigned any master product (NULL) because it only has '1' as product key and '2' is not present else it would be assigned a Kebab.
S3 is also assigned a Subway because it has product keys 30 and 31 from product table present for that order number, and product key 30 that is a parent product is assigned to at least one child product that it '31'.
S4 is not assigned any Master Product although it has all product keys for Subway (30,31) because 30 that is a parent product isn't assigned to any child product.
This is a bit convoluted, but here is a working version:
declare #p table(MasterProduct nvarchar(50), Productkey int, ProductName nvarchar(50))
declare #o table(OrderLineNumberKey int, Orderno int, Productkey int, ParentProduct int)
insert #p values('kebab',1,'chicken'),('kebab',2,'mayo'),('subway',30,'bread'),('subway',31,'salad')
insert #o values(1,1,1,null),(2,1,2,1),(3,1,4,1),(4,1,7,null)
insert #o values(1,2,1,null),(2,2,9,null),(3,2,14,1)
insert #o values(1,3,30,null),(2,3,31,30),(3,3,35,null)
insert #o values(1,4,30,null),(2,4,31,null),(3,4,39,null)
;with ParentChild as (
select o.orderno, o.ProductKey as TopProductKey, o.productkey, p.masterproduct
from #o o
join #p p on p.productKey=o.ProductKey
where parentproduct is null
union all
select pc.orderno,pc.ProductKey as TopProductKey,o.productkey,p.masterproduct
from #o o
join #p p on p.productKey=o.ProductKey
join ParentChild pc on pc.orderno=o.orderno and pc.ProductKey=o.ParentProduct
)
select case when HaveAll=1 then ordercheck.masterproduct end as masterproduct,
o.orderno, o.productkey,o.parentproduct
from #o o
left join(
select orderno, masterproduct, max(HaveAll) as HaveAll from (
select pc1.orderno, p.masterproduct, min(case when pc2.productkey is null then 0 else 1 end) as HaveAll
from ParentChild pc1
join #p p on p.masterproduct=pc1.masterproduct
left join ParentChild pc2 on pc2.orderno=pc1.orderno and pc2.MasterProduct=p.MasterProduct and pc2.productkey=p.productkey and pc2.topproductkey=pc1.topproductkey
where pc1.TopProductKey=pc1.ProductKey
group by pc1.orderno, p.masterproduct, pc1.TopProductKey
) q
group by orderno, masterproduct
) ordercheck on ordercheck.orderno=o.orderno
To explain it, firstly in the CTE we look recursively through the orders looking for all possible parents with a null parent, and finding all their child rows. You will see that order 4 has 2 possible candidates which complicates things.
Then in the main query, there is an inner sub query that compares the all the parent+child rows against the product model, and checks that we have all products. It has to do this for each of the candidate parents, and then rolls that up a second time for the overall order. It has to do this in case there were two candidate parents, one of which was satisfied and one wasnt.
Finally we join this to the orders to return the masterproduct if we had all the products, or null if we didnt.

Where Not Exists With One to Many Relationship

I am having a hard time figuring out how to ask this question, so I'll just go straight to the example code. Let's say I have these tables:
create table Item
(
ItemId int identity(1,1),
Name nvarchar(256)
)
create table ItemSale
(
ItemSaleId int identity(1,1),
ItemId int,
Price decimal,
CategoryId tinyint
)
What I want to retrieve is the list of ItemSale records that are not in a given CategoryId. The complication, at least for me, is that if a record exists in ItemSale for a given Item, I do not want to see any records for that Item.
So if I have this data:
insert into Item(Name)
select N'Widget' union all
select N'Foo' union all
select N'Buzz'
insert into ItemSale(ItemId, Price, CategoryId)
select 1, 9.95, 1 union all
select 1, 19.95, 2 union all
select 3, 99.99, 3
And the CategoryId I want to filter out is 1, then I don't want to see any records for ItemId 1 ("Widget"). So, with that sample data, I would only see the ItemSale record for Item with ID 3.
I know that my solution will most likely involve some sort of NOT EXISTS OR LEFT JOIN but I'm struggling with how to filter out all records instead of just the specific record that matches my criteria. What am I missing?
SQLFiddle: http://sqlfiddle.com/#!3/79c58
I might be over simplifying your problem, but I think this would work:
SELECT *
FROM ItemSale i
WHERE NOT EXISTS
( SELECT 1
FROM ItemSale i2
WHERE i.ItemID = i2.ItemID
AND i2.CategoryID = 1
);
Example on SQL Fiddle

Database design - Multiple 1 to many relationships

What would be the best way to model 1 table with multiple 1 to many relatiionships.
With the above schema if Report contains 1 row, Grant 2 rows and Donation 12. When I join the three together I end up with a Cartesian product and result set of 24. Report joins to Grant and creates 2 rows, then Donation joins on that to make 24 rows.
Is there a better way to model this to avoid the caresian product?
example code
DECLARE #Report
TABLE (
ReportID INT,
Name VARCHAR(50)
)
INSERT
INTO #Report
(
ReportID,
Name
)
SELECT 1,'Report1'
DECLARE #Grant
TABLE (
GrantID INT IDENTITY(1,1) PRIMARY KEY(GrantID),
GrantMaker VARCHAR(50),
Amount DECIMAL(10,2),
ReportID INT
)
INSERT
INTO #Grant
(
GrantMaker,
Amount,
ReportID
)
SELECT 'Grantmaker1',10,1
UNION ALL
SELECT 'Grantmaker2',999,1
DECLARE #Donation
TABLE (
DonationID INT IDENTITY(1,1) PRIMARY KEY(DonationID),
DonationMaker VARCHAR(50),
Amount DECIMAL(10,2),
ReportID INT
)
INSERT
INTO #Donation
(
DonationMaker,
Amount,
ReportID
)
SELECT 'Grantmaker1',10,1
UNION ALL
SELECT 'Grantmaker2',3434,1
UNION ALL
SELECT 'Grantmaker3',45645,1
UNION ALL
SELECT 'Grantmaker4',3,1
UNION ALL
SELECT 'Grantmaker5',34,1
UNION ALL
SELECT 'Grantmaker6',23,1
UNION ALL
SELECT 'Grantmaker7',67,1
UNION ALL
SELECT 'Grantmaker8',78,1
UNION ALL
SELECT 'Grantmaker9',98,1
UNION ALL
SELECT 'Grantmaker10',43,1
UNION ALL
SELECT 'Grantmaker11',107,1
UNION ALL
SELECT 'Grantmaker12',111,1
SELECT *
FROM #Report r
INNER JOIN
#Grant g
ON r.ReportID = g.ReportID
INNER JOIN
#Donation d
ON r.ReportID = d.ReportID
Update 1 2011-03-07 15:20
Cheers for the feedback so far, to add to this scenario there are also 15 other 1 to many relationships coming from the one report table. These tables can't for various business reasons be grouped together.
Is there any relationship at all between Grants and Donations? If there isn't, does it make sense to pull back a query that shows a pseudo relationship between them?
I'd do one query for grants:
SELECT r.*, g.*
FROM #Report r
JOIN #Grant g ON r.ReportID = g.ReportID
And another for donations:
SELECT r.*, d.*
FROM #Report r
JOIN #Donation d ON r.ReportID = d.ReportID
Then let your application show the appropriate data.
However, if Grants and Donations are similar, then just make a more generic table such as Contributions.
Contributions
-------------
ContributionID (PK)
Maker
Amount
Type
ReportID (FK)
Now your query is:
SELECT r.*, c.*
FROM #Report r
JOIN #Contribution c ON r.ReportID = c.ReportID
WHERE c.Type = 'Grant' -- or Donation, depending on the application
If you're going to join on ReportID, then no, you can't avoid a lot of rows. When you omit the table "Report", and just join "Donation" to "Grant" on ReportId, you still get 24 rows.
SELECT *
FROM Grant g
INNER JOIN
Donation d
ON g.ReportID = d.ReportID
But the essential point is that it doesn't make sense in the real world to match up donations and grants. They're completely independent things that essentially have nothing to do with each other.
In the database, the statement immediately above will join each row in Grants to every matching row in Donation. The resulting 24 rows really shouldn't surprise you.
When you need to present independent things to the user, you should use a report writer or web application (for example) that selects the independent things, well, independently. Select donations and put them into one section of a report or web page, then select grants and put them into another section of the report or web page, and so on.
If the table "Report" is supposed to help you record which sections go into a particular report, then you need a structure more like this:
create table reports (
reportid integer primary key,
report_name varchar(35) not null unique
);
create table report_sections (
reportid integer not null references reports (reportid),
section_name varchar(35), -- Might want to reference a table of section names
section_order integer not null,
primary key (reportid, section_name)
);
The donation and grant tables look almost identical. You could make them one table and add a column that is something like DonationType. Would reduce complexity by 1 table. Now if donations and grants are completely different and have different subtables associated with them then keeping them seperate and only joining on one at a time would be ideal.