How do I show all records from one table for each semi-related record in another? - sql

This problem is extremely difficult to explain and I did not even know how to title it correctly so I do apologise about that in advance.
I have a view of products which is as follows:
Product
ProductId
ProductName
In my database, I have a ratecard table and a ratecard product table. A ratecard may be titled "Tier 1 Customers" and the corresponding RatecardProduct records would be prices for products for that particular ratecard. It may only contain prices for a few products and not all of them.
Ratecard
RatecardId
RatecardName
RatecardProduct
RatecardProductId
RatecardId
ProductId
UnitPrice
The problem is that I need to create a view which displays all products for all ratecards. If the ratecard / product combination does not have a corresponding unit price in my ratecardproduct table, it should show NULL or 0.
Imagine I have 10 products and 4 ratecards; the view would contain 40 records, even if the RatecardProduct table was completely empty
The reason I need to do this is because I am populating a gridview on viewing a ratecard and I do not want to have to do a round trip for each row to ascertain if there is a corresponding price.
Thank you so much in advance.

Generate all the rows. Then use left join to bring in the data:
select p.*, r.*, coalesce(rp.unitprice, 0) as unitprice
from products p cross join
ratecards r left join
ratecardproduct rp
on rp.productid = p.productid and rp.ratecardid = r.ratecardid;
Or don't use coalesce() if you want NULL.

Related

Find item name along with the seller id and buyer id such that the seller has sold the item to the buyer

Hi IM trying to build a query for the following sentence for sql.
For each seller and each item sold by the seller, find the total amount sold.
I have 3 tables but not sure If i have to use them all. I have a feeling I need to use at least three tables to get this query but I keep getting an error. Also, I keep getting an error when I try to the following:
select selleruserid, itemid, sum(price) total
from sales_fact s
join items_dim i on i.itemid = s.itemid
join sellers_dim d on d.userid = s.selleruserid
group by selleruserid, itemid
I added a picture below of my tables.
All the information you want is in the fact table, so the other tables do not seem necessary:
select sf.selleruserid, sf.itemid, sum(sf.price) as total
from sales_fact s
group by sf.selleruserid, sf.itemid;
You would need to join in the other tables if you needed other information from the dimension, such as the name.

Multiple Conditions in SQL join, shopping cart example

Cart Example
I have two tables that I am working with related to a shopping cart. The first is the product table, and then the cart table. Note i have only included the relevant columns. I want to pull records from the product table that have not already been added to my cart. I can do it when there is only 1 memberid, but as you get more members, products will overlap so that is not going to work. I need to constrain my left outer join to include the member id also. This is what i have so far and this works. The issue is, how do i then constrain it to not show me products from the product table that have already been added to "my" cart which means to exclude where the memberid = 5
SELECT top 50
product.productid
FROM [shopping].[dbo].[product] with (NOLOCK)
LEFT OUTER JOIN cart with (NOLOCK) on product.productid = cart.productid
where 1=1
and cart.productid is null
My results should pull records 1 - 5 only.
I am clarifying what i want per Universus' comment. I want the query to display records from the product table that HAVE NOT already been added to cart. So i want the results to exclude records that are already in the cart for my memberid. This is not for an actual shopping cart application but i thought it would be best explained this way. I hope this clarifies the question.
What about this:
SELECT top 50 product.productid
FROM [shopping].[dbo].[product]
WHERE product.productid NOT IN (SELECT productID
FROM Card
WHERE MemberID = <current member ID value>
) A
Meaning "show me all products that were not selected by the current member". This is assuming that you clean the Cart table once the transaction is completed.

Applying calculations based on a number of criteria stored in separate tables?

What I need to create is a table containing "Rules" such as overriding prices and applying percentage increases to the price of stock.
For Example:
Sales Price is select from the table containing information about products, then the system needs to check another table to see if that Customer/Product/Product Category has any price rules set against it, such as percentage discount or set price to be overridden to.
How do I get access to first of all check if the customer in question exists in the table, then if the product exists and then if the category exists; and then apply the price change that is stored?
So far we have a PriceRules table that contains the headers:
RuleID | CustomerID | Product Code | Category | Price | Percentage | DateApplied | AppliedBy
The plan is to store the different variables in each column and then search based on the columns.
I'm sure this sounds really confusing so I will be around to answer queries as quickly as possible.
Thanks in advance,
Bob P
You can get these results using SQL JOINs:
SELECT ...
Product.ProductPrice as Price,
CustomerRules.ProductPriceRules as Rules
FROM Product
LEFT JOIN Customer
ON ...
LEFT JOIN CustomerRules
ON Product.ProductID = CustomerRules.ProductID
AND Customer.CustomerID = CustomerRules.CustomerID
LEFT JOIN will return ONLY matching results if any exist, if record does not exist all CustomerRules fields will contain NULL values

many to many select query

I'm trying to write code to pull a list of product items from a SQL Server database an display the results on a webpage.
A requirement of the project is that a list of categories is displayed at the right hand side of the page as a list of checkboxes (all categories selected by default) and a user can uncheck categories and re-query the database to view products's in only the categories they want.
Heres where it starts to get a bit hairy.
Each product can be assinged to multiple categories using a product categories table as below...
Product table
[product_id](PK),[product_name],[product_price],[isEnabled],etc...
Category table
[CategoryID](PK),[CategoryName]
ProductCagetory table
[id](PK),[CategoryID](FK),[ProductID](FK)
I need to select a list of products that match a set of category ID's passed to my stored procedure where the products have multiple assigned categories.
The categort id's are passed to the proc as a comma delimited varchar i.e. ( 3,5,8,12 )
The SQL breaks this varchar value into a resultset in a temp table for processing.
How would I go aout writing this query?
One problem is passing the array or list of selected categories into the server. The subject was covered at large by Eland Sommarskog in the series of articles Arrays and Lists in SQL Server. Passing the list as a comma separated string and building a temp table is one option. There are alternatives, like using XML, or a Table-Valued-Parameter (in SQL Server 2008) or using a table #variable instead of a #temp table. The pros and cons of each are covered in the article(s) I linked.
Now on how to retrieve the products. First things first: if all categories are selected then use a different query that simply retrieves all products w/o bothering with categories at all. This will save a lot of performance and considering that all users will probably first see a page w/o any category unselected, the saving can be significant.
When categories are selected, then building a query that joins products, categories and selected categories is fairly easy. Making it scale and perform is a different topic, and is entirely dependent on your data schema and actual pattern of categories selected. A naive approach is like this:
select ...
from Products p
where p.IsEnabled = 1
and exists (
select 1
from ProductCategories pc
join #selectedCategories sc on sc.CategoryID = pc.CategoryID
where pc.ProductID = p.ProductID);
The ProductsCategoriestable must have an index on (ProductID, CategoryID) and one on (CategoryID, ProductID) (one of them is the clustered, one is NC). This is true for every solution btw. This query would work if most categories are always selected and the result contains most products anyway. But if the list of selected categories is restrictive then is better to avoid the scan on the potentially large Products table and start from the selected categories:
with distinctProducts as (
select distinct pc.ProductID
from ProductCategories pc
join #selectedCategories sc on pc.CategoryID = sc.CategoryID)
select p.*
from Products p
join distinctProducts dc on p.ProductID = dc.ProductID;
Again, the best solution depends largely on the shape of your data. For example if you have a very skewed category (one categoru alone covers 99% of products) then the best solution would have to account for this skew.
This gets all products that are at least in all of the desired categories (no less):
select * from product p1 join (
select p.product_id from product p
join ProductCategory pc on pc.product_id = p.product_id
where pc.category_id in (3,5,8,12)
group by p.product_id having count(p.product_id) = 4
) p2 on p1.product_id = p2.product_id
4 is the number of categories in the set.
This gets all products that are exactly in all of the desired categories (no more, no less):
select * from product p1 join (
select product_id from product p1
where not exists (
select * from product p2
join ProductCategory pc on pc.product_id = p2.product_id
where p1.product_id = p2.product_id
and pc.category_id not in (3,5,8,12)
)
group by product_id having count(product_id) = 4
) p2 on p1.product_id = p2.product_id
The double negative can be read as: get all products for which there are no categories that are not in the desired category list.
For the products in any of the desired categories, it's as simple as:
select * from product p1 where exists (
select * from product p2
join ProductCategory pc on pc.product_id = p2.product_id
where
p1.product_id = p2.product_id and
pc.category_id in (3,5,8,12)
)
This should do. Yo don't have to break the comma delimited category ids.
select distinct p.*
from product p, productcategory pc
where p.product_id = pc.productid
and pc.categoryid in ( place your comma delimited category ids here)
This will give the products which are in any of the passed in category ids i.e., as per JNK's comment its an OR not ALL. Please specify if you want an AND i.e, the product needs to be selected only if it is in ALL the categories specified in the comma separated list.
If you need anything else than product_id from products then you can write something like this (and adding the extra fields that you need):
SELECT distinct(p.product_id)
FROM product_table p
JOIN productcategory_table pc
ON p.product_id=pc.product_id
WHERE pc.category_id in (3,5,8,12);
on the other hand if you need really just the product_id you can simply select them from productcategory_table:
SELECT distinct(product_id)
FROM productcategory_table
WHERE category_id in (3,5,8,12);
This should be fairly close to what you are looking for
SELECT product.*
FROM product
JOIN ProductCategory ON ProductCategory.ProductID = Product.product_id
JOIN #my_temp ON #my_temp.category_id = ProductCategory.CategoryID
EDIT
As noted in the comments this will produce duplicates for those products appearing in multiple categories. To correct this then specify DISTINCT before the column list. I have included all product columns in the list product.* as I do not know which columns you are looking for but you should probably change that to the specific columns that you want

SQL Syntax for Complex Scenario (Deals)

i have a complex query to be written but cannot figure it out
here are my tables
Sales --one row for each sale made in the system
SaleProducts --one row for each line in the invoice (similar to OrderDetails in NW)
Deals --a list of possible deals/offers that a sale may be entitled to
DealProducts --a list of quantities of products that must be purchased in order to get a deal
now im trying to make a query which will tell me for each sale which deals he may get
the relevant fields are:
Sales: SaleID (PK)
SaleProducts: SaleID (FK), ProductID (FK)
Deals: DealID (PK)
DealProducts: DealID(FK), ProductID(FK), Mandatories (int) for required qty
i believe that i should be able to use some sort of cross join or outer join, but it aint working
here is one sample (of about 30 things i tried)
SELECT DealProducts.DealID, DealProducts.ProductID, DealProducts.Mandatories,
viwSaleProductCount.SaleID, viwSaleProductCount.ProductCount
FROM DealProducts
LEFT OUTER JOIN viwSaleProductCount
ON DealProducts.ProductID = viwSaleProductCount.ProductID
GROUP BY DealProducts.DealID, DealProducts.ProductID, DealProducts.Mandatories,
viwSaleProductCount.SaleID, viwSaleProductCount.ProductCount
The problem is that it doesn't show any product deals that are not fulfilled (probably because of the ProductID join). i need that also sales that don't have the requirements show up, then I can filter out any SaleID that exists in this query where AmountBought < Mandatories etc
Thank you for your help
I'm not sure how well I follow your question (where does viwSaleProductCount fit in?) but it sounds like you will want an outer join to a subquery that returns a list of deals along with their associated products. I think it would go something like this:
Select *
From Sales s Inner Join SaleProducts sp on s.SaleID = sp.SaleID
Left Join (
Select *
From Deals d Inner Join DealProducts dp on d.DealID = dp.DealId
) as sub on sp.ProductID = sub.ProductID
You may need to add logic to ensure that deals don't appear twice, and of course replace * with the specific column names you'd need in all cases.
edit: if you don't actually need any information from the sale or deal tables, something like this could be used:
Select sp.SaleID, sp.ProductID, sp.ProductCount, dp.DealID, dp.Mandatories
From SaleProducts sp
Left Join DealProducts as dp on sp.ProductID = dp.ProductID
If you need to do grouping/aggregation on this result you will need to be careful to ensure that deals aren't counted multiple times for a given sale (Count Distinct may be appropriate, depending on your grouping). Because it is a Left Join, you don't need to worry about excluding sales that don't have a match in DealProducts.