LINQ to SQL with multiple joins, group by and count - linq-to-sql

I have 3 tables, Cust, Order and Item with the following relevant fields:
Cust - CustID, CustName
Order - OrderID, CustID
Item - ItemID, OrderID
I want to find the total number of orders and items for each customer.
Here is an SQL statement that generates what I want, a list of customers with the total number of orders for each customer and the total number of items ordered.
SELECT
Cust.CustID, Cust.CustName,
count(DISTINCT Order.OrderID) AS numOrders,
count(DISTINCT Item.ItemID ) AS numItems
FROM Cust
LEFT JOIN Order ON Order.CustID = Cust.CustID
LEFT JOIN Item ON Item.OrderID = Order.OrderID
GROUP BY Cust.CustID, Cust.CustName
ORDER BY numItems
My first attempt at converting this to LINQ was to just count items and came up with this:
var qry = from Cust in tblCust
join Order in tblOrder on Cust.CustID equals Order.CustID
join Item in tblItem on Order.OrderID equals Item.OrderID
group Cust by new {CustID = Cust.CustID, CustName = Cust.CustName} into grp
orderby grp.Count()
select new
{
ID = grp.Key.CustID,
Name = grp.Key.CustName,
Cnt = grp.Count()
};
With this code I get the exception:
Value cannot be null. Parameter name: inner
Am I on the right track? What would I have to do to get both counts?

For Left Joins - I suggest using a from with a where and a DefaultIfEmpty
You need to group using an anonymous type in order to group multiple parameters
Value cannot be null. Parameter name: inner
Are any of joining properties nullable?
var qry =
from Cust in tblCust
from Order in tblOrder.Where(x => Cust.CustID == x.CustID)
.DefaultIfEmpty()
from Item in tblItem.Where(x => Order.OrderID == x.OrderID)
.DefaultIfEmpty()
group new { Cust, Order.OrderId, Item.ItemId } by new { Cust.CustID, Cust.CustName } into grp
let numItems = grp.Select(x => x.ItemId).Distinct().Count()
orderby numItems
select new
{
ID = grp.Key.CustID,
Name = grp.Key.CustName,
numOrders = grp.Select(x => x.OrderId).Distinct().Count(),
numItems,
};

Related

mysql - Adding median to this query

One product ID has many prices. I'm trying to find the prices that are likely incorrect by comparing each price with the median price of the product. Currently I have min and max in the output and would like to add a single column for each product's median price. I have tried avg() but this returns: Invalid input syntax for type numeric. Any ideas?
select
distinct dp.product_id,
pc.warehouse_id as retailer_id,
pc.code,
dp.product_name,
dp.product_brand_name,
dp.product_size,
dp.product_unit_count,
pd.product_name as retailer_name,
pd.brand_name as retailer_brand_name,
pd.size as retailer_size,
pd.price,
sp.max_cost,
sp.min_cost
from dim_product as dp
join product_codes as pc on pc.product_id = dp.product_id and pc.deleted_ind = 'N'
join suspect_products as sp on sp.product_id = dp.product_id
left join (
select
nvl(json_extract_path_text(rf."raw",'brand_name'),dei.brand_name) as brand_name,
nvl(json_extract_path_text(rf."raw",'name'),dei.name) as product_name,
nvl(json_extract_path_text(rf."raw",'size'), dei.size) as size,
nvl(json_extract_path_text(rf."raw",'cost_price_per_unit'),dei.cost_price_per_unit::VARCHAR) as price,
nvl(rf.code,dei.lookup_code) as code,
nvl(rf.retailer_id,dei.warehouse_id) as retailer_id
from product_retailer_file_data as rf
left join (
select
d.brand_name,
d.name,
d.size,
d.cost_price_per_unit,
d.lookup_code,
d.warehouse_id
from data_entry_items as d
join (
select lookup_code, max(created_at) as max_date
from data_entry_items
where created_at > current_date-720
group by 1) as m on m.lookup_code = d.lookup_code and m.max_date = d.created_at
) as dei on dei.lookup_code = rf.code and dei.warehouse_id = rf.retailer_id
) as pd on pd.code = pc.code and pd.retailer_id = pc.warehouse_id
order by 1;

How to get nested two values from nested select ? mysql

Query:
SELECT business.bussId,
(select count(invoices.userId) from invoice where invoice.userId = '3000' )
as invoiceCount,
(select SUM(invoices.price) from invoice where invoice.userId = '3000' )
as invoiceprice ,
FROM business WHERE business.bussId=100
How could I get invoice price and invoiceCount using one nested select ?
Move the subquery to the from clause:
SELECT b.bussId, i.invoiceCount, i.invoiceprice
FROM business b cross join
(select count(i.userId) as invoiceCount, SUM(i.price) as invoiceprice
from invoice i
where i.userId = '3000'
) i
WHERE b.bussId = 100;
You can actually write this without the subquery, but your question is specifically about using subqueries.
That form would be:
SELECT b.bussId, count(i.userId) as invoiceCount, SUM(i.price) as invoiceprice
FROM business b left join
invoice i
on i.userId = '3000' and b.bussId = 100
GROUP BY b.bussId;

MySql sort by highest value with inner join and subquery

I have a query that has a sub query that returns a count of records from another table, I'm having trouble ordernar the largest number of this counter
SELECT respostas.id,
respostas.cmm,
respostas.topico,
respostas.usuario,
respostas.resposta,
perfis.nome,
perfis.sobrenome,
respostas.datahora,
(
SELECT count(id)
FROM likes
WHERE respostas.id = resposta
) AS total
FROM respostas
INNER JOIN perfis ON respostas.usuario = perfis.id
INNER JOIN likes ON respostas.topico = likes.topico
WHERE respostas.cmm = 28
AND respostas.topico = 38
ORDER BY respostas.id ASC, total ASC
LIMIT 0,20`enter code here`
I want to sort by the total column and can not.
Sorting by total does not work, only ordered by id
You can choose which column to order by numerically:
SELECT
(
SELECT count(id)
FROM likes
WHERE respostas.id = resposta
) AS total,
respostas.id,
respostas.cmm,
respostas.topico,
respostas.usuario,
respostas.resposta,
perfis.nome,
perfis.sobrenome,
respostas.datahora
FROM respostas
INNER JOIN perfis ON respostas.usuario = perfis.id
INNER JOIN likes ON respostas.topico = likes.topico
WHERE respostas.cmm = 28
AND respostas.topico = 38
ORDER BY 1, respostas.id
LIMIT 0,20
What is the purpose of Order By 1 in SQL select statement?

MySQL Filter result again

The goal here is to:
1. Fetch the row with the most recent date from EACH store for EACH ingredient.
2. From this result, compare the prices to find the cheapest store for EACH ingredient.
I can accomplish either the first or second goal in separate queries, but not in the same.
How can i filter out a selection and then apply another filter on the previous result?
EDIT:
I've been having problems with results that i get from MAX and MIN since it just fetches the rest of the data arbitrarily. To avoid this im supposed to join tables on multiple columns (i guess). Im not sure how this will work with duplicate dates etc.
I've included an image of a query and its output data.
If we use ingredient1 as an example, it exists in three separate stores (in one store twice on different dates).
In this case the cheapest current price for ingredient1 would be store3. If the fourth row dated 2013-05-25 was even cheaper, it would still not "win" due to it being out of date.
(Disregard brandname, they dont really matter in this problem.)
Would appreciate any help/input you can offer!
This question is really interesting!
So, first, we get the row with the most recent date from EACH store for EACH ingredient. (It is possible that the most recent dates from each store can be different.)
Then, we compare the prices from each store (regardless of the date) to find the least price for each ingredient.
The query below uses the GROUP_CONCAT function in good measure. Here's a SO question regarding the use of the function.
SELECT
i.name as ingredient_name
, MIN(store_price.price) as price
, SUBSTRING_INDEX(
GROUP_CONCAT(store_price.date ORDER BY store_price.price),
',',
1
) as date
, SUBSTRING_INDEX(
GROUP_CONCAT(s.name ORDER BY store_price.price),
',',
1
) as store_name
, SUBSTRING_INDEX(
GROUP_CONCAT(b.name ORDER BY store_price.price),
',',
1
) as brand_name
FROM
ingredient i
JOIN
(SELECT
ip.ingredient_id as ingredient_id
, stip.store_id as store_id
, btip.brand_id as brand_id
, CONVERT(SUBSTRING_INDEX(
GROUP_CONCAT(ip.ingredient_price_id ORDER BY ip.date DESC),
',',
1
), UNSIGNED INTEGER) as ingredient_price_id
, MAX(ip.date) as date
, CONVERT(SUBSTRING_INDEX(
GROUP_CONCAT(ip.price ORDER BY ip.date DESC),
',',
1
), DECIMAL(5,2)) as price
FROM ingredient_price ip
JOIN store_to_ingredient_price stip ON ip.ingredient_price_id = stip.ingredient_price_id
JOIN brand_to_ingredient_price btip ON ip.ingredient_price_id = btip.ingredient_price_id
GROUP BY
ip.ingredient_id
, stip.store_id) store_price
ON i.ingredient_id = store_price.ingredient_id
JOIN store s ON s.store_id = store_price.store_id
JOIN brand b ON b.brand_id = store_price.brand_id
GROUP BY
store_price.ingredient_id;
You can check the implementation on this SQL Fiddle.
The version below, which ignores the brand, is slightly smaller:
SELECT
i.name as ingredient_name
, MIN(store_price.price) as price
, SUBSTRING_INDEX(
GROUP_CONCAT(store_price.date ORDER BY store_price.price),
',',
1
) as date
, SUBSTRING_INDEX(
GROUP_CONCAT(s.name ORDER BY store_price.price),
',',
1
) as store_name
FROM
ingredient i
JOIN
(SELECT
ip.ingredient_id as ingredient_id
, stip.store_id as store_id
, CONVERT(SUBSTRING_INDEX(
GROUP_CONCAT(ip.ingredient_price_id ORDER BY ip.date DESC),
',',
1
), UNSIGNED INTEGER) as ingredient_price_id
, MAX(ip.date) as date
, CONVERT(SUBSTRING_INDEX(
GROUP_CONCAT(ip.price ORDER BY ip.date DESC),
',',
1
), DECIMAL(5,2)) as price
FROM ingredient_price ip
JOIN store_to_ingredient_price stip ON ip.ingredient_price_id = stip.ingredient_price_id
GROUP BY
ip.ingredient_id
, stip.store_id) store_price
ON i.ingredient_id = store_price.ingredient_id
JOIN store s ON s.store_id = store_price.store_id
GROUP BY
store_price.ingredient_id;
References:
Simulating First/Last aggregate functions in MySQL
This probably needs a couple of sub queries joined together.
This isn't tested (as I don't have your table definitions, nor any test data), but something like this:-
SELECT i.name AS ingredient,
ip.price,
ip.date,
s.name AS storename,
b.name AS brandname
FROM ingredient i
INNER JOIN ingredient_price ip
ON ingredient.ingredient_id = ingredient_price.ingredient_id
INNER JOIN store_to_ingredient_price stip
ON ingredient_price.ingredient_price_id = store_to_ingredient_price.ingredient_price_id
INNER JOIN store s
ON store_to_ingredient_price.store_id = store.store_id
INNER JOIN brand_to_ingredient_price btip
ON ingredient_price.ingredient_price_id = brand_to_ingredient_price.ingredient_price_id
INNER JOIN brand b
ON brand_to_ingredient_price.brand_id = brand.brand_id
INNER JOIN
(
SELECT i.ingredient_id,
stip.store_id,
ip.date,
MIN(ip.price) AS lowest_price
FROM ingredient i
INNER JOIN ingredient_price ip
ON ingredient.ingredient_id = ingredient_price.ingredient_id
INNER JOIN store_to_ingredient_price stip
ON ingredient_price.ingredient_price_id = store_to_ingredient_price.ingredient_price_id
INNER JOIN
(
SELECT i.ingredient_id,
stip.store_id,
MAX(ip.date) AS latest_date
FROM ingredient i
INNER JOIN ingredient_price ip
ON ingredient.ingredient_id = ingredient_price.ingredient_id
INNER JOIN store_to_ingredient_price stip
ON ingredient_price.ingredient_price_id = store_to_ingredient_price.ingredient_price_id
GROUP BY ingredient_id, store_id
) Sub1
ON i.ingredient_id = Sub1.ingredient_id
AND stip.store_id = Sub1.store_id
AND ip.date = Sub1.latest_date
GROUP BY i.ingredient_id, stip.store_id, ip.date
) Sub2
ON i.ingredient_id = Sub2.ingredient_id
AND stip.store_id = Sub2.store_id
AND ip.date = Sub2.date
AND ip.price = Sub2.lowest_price
Try this:
SELECT `newest`.ingredient, `newest`.store,
`newest`.brand, `newest`.price, `newest`.`latest_date`
FROM
(SELECT ingredient.name AS ingredient, store.name AS store,
brand.name AS brand, ingredient_price.price,
MAX( ingredient_price.date ) AS `latest_date`
FROM ingredient
LEFT OUTER JOIN ingredient_price
ON ingredient.ingredient_id = ingredient_price.ingredient_id
LEFT OUTER JOIN store_to_ingredient_price
ON ingredient_price.ingredient_price_id = store_to_ingredient_price.ingredient_price_id
LEFT OUTER JOIN store
ON store_to_ingredient_price.store_id = store.store_id
LEFT OUTER JOIN brand_to_ingredient_price
ON ingredient_price.ingredient_price_id = brand_to_ingredient_price.ingredient_price_id
LEFT OUTER JOIN brand
ON brand_to_ingredient_price.brand_id = brand.brand_id
GROUP BY ingredient.name) `newest`
ORDER BY `newest`.price
LIMIT 1

MySql: How to check field has data in MySql?

Here is my code and result :
SELECT DISTINCT
CAL.CarListingId,
(SELECT
IF(REPLACE(carImage.ImageUrl,'~','') IS NULL,'asdf',REPLACE(carImage.ImageUrl,'~',''))
FROM
carImage
WHERE
IsMainImage = 1 AND Status = 1
AND CarListingId = CAL.CarListingId) AS ImageUrl,
CAL.ListingNumber,
CAL.Caption,
CAL.Year,
CAL.Km,
CAL.Color,
CAL.Price,
CONCAT((SELECT Name FROM City WHERE CityId IN (SELECT CityId FROM County WHERE CountyId = CAL.CountyId)),'/', (SELECT Name FROM County WHERE CountyId = CAL.CountyId)) AS Region,
CAL.Creation
FROM
carlisting AS CAL
INNER JOIN
User AS U ON U.UserId = CAL.CreatedBy
INNER JOIN
carlistingcategory AS CLC ON CLC.CarListingId = CAL.CarListingId
LEFT JOIN CarImage AS CI ON CI.CarListingId = CAL.CarListingId
ORDER BY CAL.Creation;
I use this query as a subquery in another query. I need to check this query's result if it is `NULL`. But as you can see there is no data so `IS NULL` returns false. How can I check the sub query has data ?
try this query:
SELECT DISTINCT
CAL.CarListingId,
CAL.ListingNumber,
CAL.Caption,
CAL.Year,
CAL.Km,
CAL.Color,
CAL.Price,
CONCAT((SELECT Name FROM City WHERE CityId IN (SELECT CityId FROM County WHERE CountyId = CAL.CountyId)),'/', (SELECT Name FROM County WHERE CountyId = CAL.CountyId)) AS Region,
CAL.Creation,
( case when CI.ImageUrl IS NULL then 'asdf' else CI.ImageUrl
end)
FROM
carlisting AS CAL
LEFT JOIN CarImage AS CI ON CI.CarListingId = CAL.CarListingId
INNER JOIN User AS U ON U.UserId = CAL.CreatedBy
INNER JOIN carlistingcategory AS CLC ON CLC.CarListingId = CAL.CarListingId
ORDER BY CAL.Creation;