Complex Linq filter query - sql

I have the following data structure
Store
Id
StoreName
Product
Id
Name
StoreId
Category
Id
Name
ProductCategory
Id
ProductId
CategoryId
Deal
Id
Name
DealCategory
Id
DealId
CategoryId
My requirement is to get a collection of strings representing categories and then query for stores which have a product or a deal in any of the given categories. Can anyone help as I am stumped. I get the right results if I don't filter by categories but the filter bit just seems to fail s I get no records back and I am certain that there is at least one Store with both a product and a deal in the given categories.
Here is my code so far, if I remove the where clause, I get the right data i.e. all the Stores (that have Deals or Products?! I haven't tested this discrimination yet).
from s in ctx.Store
join d in ctx.Deals on s.ID equals d.StoreID
join l in ctx.Products on s.ID equals l.StoreID
where d.DealCategories.Any(dc => categoriesList.Contains(dc.Category.Name.ToLower()))
group s by s.ID into sg
select new { Store = sg.FirstOrDefault() };

Related

Faceted search count in SQL

I'm trying to implement faceted search count in SQL. For simplicity, I'll take the data that already exists on https://www.w3schools.com/sql/trysql.asp?filename=trysql_select_all. A product has a category and a category belongs to many products, so it's a one-to-many relationship. I'm interested in filtering products by category, so if there are multiple categories selected, the query will get products whose category Id can be found in the list of Id's that the user filtered by (So it's an OR operation between categories). But this is not the challenge that I'm currently facing.
The query below tries to answer the question: For every category that exists, how many products would I get if that category was among the selected categories?
SELECT
cat.CategoryId,
p.Count
FROM Categories AS cat
LEFT JOIN (SELECT
COUNT(DISTINCT ProductId) AS Count
FROM Products AS p
WHERE p.CategoryId IN #CategoryIds
OR p.CategoryId = cat.CategoryId) AS p
The #CategoryIds is a parameter that is going to be handled by an ORM. For a more concrete scenario, you can just replace it with the list (1, 2) (so you can consider the case in which the user wants to filter all products that have the category 1 or 2).
The issue is that the word "cat" (on the last line) is not recognised so the query just throws an error.
Is there a way to make the second table recognise the first table's alias "cat" that I want to LEFT JOIN with? Or is there a better solution to this problem that I didn't take into consideration?
LEFT JOIN requires predicate. Some DBMS, like MS SQL Server, supports CROSS APPLY. This query should be equivalent to following one, ready to run on every SQL Database known to me:
SELECT
cat.CategoryId,
COUNT(ProductId)
FROM Categories AS cat
LEFT JOIN Products P ON p.CategoryId=cat.CategoryId OR p.CategoryId IN [list]
GROUP BY cat.CategoryId
Or, if you are using SQL Server:
SELECT
cat.CategoryId,
p.Count
FROM Categories AS cat
CROSS APPLY (SELECT COUNT(DISTINCT ProductId) AS Count
FROM Products AS p
WHERE p.CategoryId IN #CategoryIds
OR p.CategoryId = cat.CategoryId) AS p

List manipulating data from two tables content

I have these two tables
I would like to know how to do to list the product table: ProdID, Quantity, Name, Price
and table productUser: userId, State
The problem is that I need to also list all the information of product table and adding the UserId field with the same value and the state looks for a default value would be false ..
It is possible? could also, not to add the userId, State and drive it from my application code for assigning values​​. thanks
UPDATE:
If I understand your question correctly, you want to list specific fields from both tables but only when the child records match your criteria. If so, the query below would allow you to specify the userID and State.
SELECT p.prodId
,p.Quantity
,p.Name
,p.Price
,pu.userId
,pu.State
FROM Product p
INNER JOIN ProductUser pu
ON p.prodId = pu.prodId
WHERE pu.userID = #userID
AND pu.State = 0
--AND pu.State = #State
If my understanding is not correct, please post some sample table data and indicate which results you want returned.
Update to your update: I've defaulted State to zero in the query above. Followup question: do you want columns from both tables or are you trying to do an existence check against ProductUser to return just columns from Product?

linqpad query to connect two entities in odata

I am using linqpad. I have an ODATA connected. The entities are listed in left pane with relationships. There are two entities called Products and Customers. I have to get all the product id starting with pid and names of all customers startin with b. There is a relationship between both. Product is a child of customer. How do I do? I am trying since two days but unable to figure it out. Anyone could help?
this is the base code. i dont no what to do further.
from p in products
where p.ProductId.StartsWith("Pid")
from c in customers
where c.Name.StartsWith("B")
select new
{
p.Pid,
c.Name
};
Can you please specify how you want the relation between the two entities to affect the result of the query? Do you expect the above to only returns products (with ID starting with a given value) and then only the customers for those products which name starts with a certain value?
Such a query is not expressible in OData unfortunately. You could request all products which ID starts with a specific value, and all the related customers. And then filter the customers on the client.
For example this will get you all the products which ID starts with a certain value and all their customers:
from p in products
where p.ProductId.StartsWith("Pid")
select new Product
{
ProductId = p.ProductId,
Customers = p.Customers
};

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

Ensure in many-to-many relationship at least one relationship is primary

Sorry for the bad title, if you can think of a better one, let me know.
Many-to-many relationship using tables.
Product
ProductCategory
Category
In the ProductCategory table i have boolean column primarycategory
Each product must have a primary category.
I want to find all products in my database which don't have a primarycategory.
Note: I have assumed field names in tables other than the one you specified.
This should return a distinct list of product IDs that have no primary category. Bit fields in SQL server are numeric, so you can give them to the max() function.
select
pc.product
from
ProductCategory pc
group by
pc.product
having
max(pc.primarycategory) = 0
The above query assumes that all products have at least one category. If not, try the following:
select
pc.product
from
Product p
left join
ProductCategory pc on p.id = pc.product
group by
pc.product
having
max(isnull(pc.primarycategory, 0)) = 0
Assuming true = value 1, try this:
Select Product From Product p
Where Not Exists (Select * From ProductCategory
Where Product = p.Product
And primarycategory = 1 )
but if you have control over this database, Move the PrimaryCategory column to the Products table, (and populate it with the category identifier itself, not a boolean), that is where this belongs in a properly normalized schema...