First Best Match Not Already Used - sql

I'm not sure how to express this in SQL, if it's even possible, or what to even call it.
I want, for each record in Table A, the first best matching record in Table B that wasn't already picked as a best match. For example, suppose I have a Generic Shopping List and a Food Menu:
Table A Table B
Generic Shopping List Food Menu
--------------------- ----------------------
Food Type Food Food Type
--------------------- ----------------------
Meat Tomatoes Vegetable
Meat Lettuce Vegetable
Vegetable Bacon Vegetable
Vegetable Bacon Meat
Vegetable Beef Meat
Vegetable Apple Fruit
Fruit Orange Fruit
Fruit Bacon Fruit
Dairy Milk Dairy
Cheese Dairy
Yogurt Dairy
With a query or join, it's easy to get the Top 1 match:
Table/Query C
Automagic Shopping
------------------
Food
------------------
Bacon
Bacon
Tomatoes
Tomatoes
Tomatoes
Tomatoes
Apple
Apple
Milk
I know how to do that, and because I like bacon, I could live with this. Unfortunately, I really need the full breadth of the available food options, such that I have slots available for it.
Table/Query C
Better Magic Shopping
---------------------
Food
---------------------
Bacon
Beef
Tomatoes
Lettuce
Bacon
<NULL - No More Available Matches - Don't Care>
Apple
Orange
Milk
If this can be done in Access, great. If it can't be done in Access, but it can be done in another product, it isn't ideal, but it's workable.
Thanks.

This is a way to do it in SQL Server:
SELECT t1.FoodType, t2.Food
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY FoodType ORDER BY FoodType) AS rn
FROM #tableA ) AS t1
LEFT JOIN (
SELECT *, ROW_NUMBER() OVER (PARTITION BY FoodType ORDER BY FoodType) AS rn
FROM #tableB) AS t2 ON t1.FoodType = t2.FoodType AND t1.rn = t2.rn
Below are, side by side, the table expressions computed by the two subqueries, t1, t2:
Results for t1: Results for t2:
FoodType rn Food FoodType rn
--------------- --------------------------
Dairy 1 Milk Dairy 1
Fruit 1 Cheese Dairy 2
Fruit 2 Yogurt Dairy 3
Meat 1 Apple Fruit 1
Meat 2 Orange Fruit 2
Vegetable 1 Bacon Fruit 3
Vegetable 2 Bacon Meat 1
Vegetable 3 Beef Meat 2
Vegetable 4 Tomatoes Vegetable 1
Lettuce Vegetable 2
Bacon Vegetable 3
Doing a LEFT JOIN on FoodType and rn gets you what you want.

Access allows a NOT IN clause. Just write the query to get the TOP 1 match. Then include this query as a subquery inside the NOT IN clause.
Select * From Table_A A, Table_B B Where A.Food_Type = B.Food_Type And B.Food Not In (Select Top 1 D.Food From Table_A D, Table_B C Where D.Food_Type = D.Food_Type And C.Food_Type = 'give your criterion value here')
Note you might want to a suitable Order By clause to describe how you determine what is a best match and what isn't.

Related

SQL query show customers who bought apples, but not potatoes

Not sure how to explain this..
I have a similar table, but i have simplified it with the following:
I have a table of goods shipped to different cusotmers. Some have bought apples only, others have bought apples and potates.
I want an SQL query to return only customers where "To be billed" = Yes AND the customer hasnt bought any vegetables.
So for example if the table looks like this:
Item
Name
Group
To_be_billed
CustomerNo.
2000
Apple
Fruit
Yes
1
2000
Apple
Fruit
No
2
2000
Apple
Fruit
No
3
2000
Apple
Fruit
Yes
4
2000
Apple
Fruit
Yes
5
4000
Potato
Vegetable
No
2
4000
Potato
Vegetable
No
4
I want the query to return:
Item
Name
Group
To_be_billed
CustomerNo.
2000
Apple
Fruit
Yes
1
2000
Apple
Fruit
Yes
5
The reason 4 has bought apples, and is to be billed, but the customer also bought Potatoes, so is to be ignored...
You can create a CTE to check for CustomerNo.s that you need to ignore, and then use not exists:
with bought_veg as
(
select "CustomerNo."
from tbl
where tbl."Group" like 'Vegetable'
)
select tbl.*
from tbl
where not exists (select 1 from bought_veg where tbl."CustomerNo." = bought_veg."CustomerNo.")
and tbl.To_be_billed = 'Yes'
Example without CTE:
select tbl.*
from tbl
where not exists (select "CustomerNo." from tbl t2 where tbl.[CustomerNo.] = t2.[CustomerNo.] and "Group" like 'Vegetable')
and tbl.To_be_billed = 'Yes'

SQL server query - return invoice that only has certain records

Say I have the following record table (table name: SALESINVOICE):
invoiceid item itemgroup
--------- ---- ---------
12345 apple fruit
12345 banana fruit
12345 banana fruit
12345 chocolate bar candy
24680 apple fruit
24680 banana fruit
24680 orange fruit
36912 pear fruit
36912 cherry fruit
98765 strawberry fruit
98765 bread grain
I would like to query the table so it will return the "invoiceid" that only has "fruit" itemgroup and no other itemgroup. So the output would look like:
invoiceid item itemgroup
--------- ---- ---------
24680 apple fruit
24680 banana fruit
24680 orange fruit
36912 pear fruit
36912 cherry fruit
How would I go about querying this in SQL Server 2016? My apologies for not providing a work in progress code. I hope the community can help me out.
Thank.
You can use not exists:
select t.*
from t
where not exists (select 1
from t t2
where t2.invoiceid = t.invoiceid and
t2.itemgroup <> 'fruit'
);

How Do I Filter My Results Based Off Of Other Entries?

I have written a statement that retrieves the person name, as well as the food they've eaten.
Person | Food Eaten
John | Cake
Jack | Ice Cream
Louis | Hot Dog
John | Pineapple Pizza
Now that I've retrieved what foods people have eaten, I would like to remove anyone who has eaten Pineapple Pizza from my list.
What type of clause would I want to use to create a unique list of people who haven't eaten Pineapple Pizza?
You can use not exists :
select t.*
from table t
where not exists (select 1
from table t1
where t1.person = t.person and t1.food = 'Pineapple Pizza'
);
If you know the exact value of the food people have eaten, you can exclude it when you write your WHERE clause.
SELECT t.person, t.food_eaten
FROM eaten_tbl t
WHERE t.food_eaten <> 'Pineapple Pizza';

How do I select records from Sqlite table partially grouped?

I have a table like this. And I want to select Fruits grouped together based on the group_id but not the Vegetables and Nuts. For Vegetable and Nuts, I want them all.
group_id name type
-----------------------------------
1 Green Apple Fruit
1 Red Apple Fruit
1 Blue Apple Fruit
2 Green Peas Vegetable
2 Snow Peas Vegetable
2 Another Pea Vegetable
3 Ground Nut Nuts
3 Peanut Nuts
4 Carrot Vegetable
This is how I have tried right now. This works well, but I want to know if there is any simpler approach.
select * from Grocessaries GROUP BY group_id HAVING type in ('Fruit', 'Drinks')
UNION all
select * from Grocessaries where type in ('Vegetable', 'Nuts')
Basically, I want the result something like this (grouped Fruits and all Vegetables and Nuts)
group_id name type
-----------------------------------
1 Green Apple Fruit
2 Green Peas Vegetable
2 Snow Peas Vegetable
2 Another Pea Vegetable
3 Ground Nut Nuts
3 Peanut Nuts
4 Carrot Vegetable
Since you're handling Fruits (and Drinks) specially in the UI the actual name returned for them isn't that important so you could do
SELECT group_id,
CASE type
WHEN 'Fruits' THEN 'Fruits' -- or whatever you want to display
WHEN 'Drinks' THEN 'Drinks'
ELSE name
END NameGrouped,
type
FROM Grocessaries
GROUP BY group_id, NameGrouped, type

Using a query to return the most frequent value and the count within a group using SQL in MS Access

Say I have a table showing the type of fruit consumed by an individual over a 24 hour period that looks like this:
Name Fruit
Tim Apple
Tim Orange
Tim Orange
Tim Orange
Lisa Peach
Lisa Apple
Lisa Peach
Eric Plum
Eric Orange
Eric Plum
How would I get a table that shows only the most consumed fruit for each person, as well as the number of fruits consumed. In other words, a table that looks like this:
Name Fruit Number
Tim Orange 3
Lisa Peach 2
Eric Plum 2
I tried
SELECT Name, Fruit, Count(Fruit)
FROM table
GROUP BY Name
But that returns an error because Name needs to be in the GROUP BY statement as well. Every other method I've tried returns the counts for ALL values rather than just the maximum values. MAX(COUNT()) doesn't appear to be a valid statement, so I'm not sure what else to do.
This is a pain, but you can do it. Start with your query and then use join:
SELECT n.Name, n.Fruit
FROM (SELECT Name, Fruit, Count(Fruit) as cnt
FROM table as t
GROUP BY Name, Fruit
) as t INNER JOIN
(SELECT Name, max(cnt) as maxcnt
FROM (SELECT Name, Fruit, Count(Fruit) as cnt
FROM table
GROUP BY Name, Fruit
) as t
GROUP BY Name
) as n
ON t.name = n.name and t.cnt = n.maxcnt;