Designing database - sql

I am finding it difficult to decide on an efficient design of the database. My application would get a number of ingredients(table) from the user and check with the database to find the recipe that could be prepared from the list of ingredients that the user provides.
My initial design is
Useringredients(ing_id,ing_name..);
the recipe database would be
recipe(rec_id,rec_text,...);
items_needed(rec_id,item_id,...);
items(item_id,item_name);
Is this a good way ? If so how will i be able to query to retrieve the recipes from the list of user ingredients.
Help would be very much appreciated.

This design could work. You have one table recording recipes, one recording items and one recording the many-to-many relationship between the two (though I would work on your naming conventions to keep things consistent).
To get any recipes that contain at least one item in your list, you could use the following:
Select rec.rec_id,
Count(itn.item_id) as [NumMatches]
From recipe as rec
Join items_needed as itn on itn.rec_id = rec.rec_id
Where itn.item_id in (comma-delimited-list-of-itemIDs)
Group By rec.rec_ID
Having Count(itn.item_id) > 0
Order By Count(itn.item_id) desc
This returns any recipes that contain at least some of the items that are selected, sorted with the first recipes having the highest number of matches.

The following query should give you a list of unique recipes using any one of the ingredients the user searches for
select distinct rec_id,rec_text,ii.item_name
from recipe rr
join items_needed itn on itn.rec_id=rr.rec_id
join items ii on ii.item_id=itn.item_id
join userIngredients ui on ui.ing_id=ii.item_id
Yaakov's query looks like it will handle the situation where you want all ingredients. You might be able to replace (comma-delimited-list-of-itemIDS) with (select ing_id from userIngredients)

Related

SQL Database "Operation must use an updateable query" Workaround

So my basic goal is to create a database for a shopsystem, which is my task to do for my IT course. I tried to create a UPDATE-Query, that collects all the Sale Positions ("tblPosition.PositionAnzahl") ordered with a SELECT-Query and groups it by the products ordered, to have an overview about how often each product has been sold.
I want to do this to keep track of how many items are still left in the inventory.
The Query was supposed to update 1 field ("tblArtikel.ArtikelVerkauft") in my table "tblArtikel", in which all my articles and their information is stored.
However, i just found out that you cannot run UPDATE-Queries, that use SELECT-Query data, as i get a error, that says "Operation must use an updateable query".
This is the code i used for the query:
UPDATE tblArtikel as a JOIN
(SELECT p.PositionArtikelID, Sum(p.PositionAnzahl) AS SumOfPositionAnzahl
FROM tblPositionen as p
GROUP BY p.PositionArtikelID
) p
ON a.ArtikelID = p.PositionArtikelID
SET ArtikelVerkauft = p.SumOfPositionAnzahl;
Is there another way to keep track of all the Items left in my inventory, apart from doing what i did?
Here are screenshots of the 2 tables (the depending fields are circled red):
tblPositionen with field PositionAnzahl
tblArtikel with field ArtikelVerkauft
I have not worked with SQL before and only learned about it during 45 min, so ther emight be an easy way for this, but i would still appreciate every answer from you guys.

Active Record where join table record doesn't exist

I am trying to get a list of all records that don't exist in a join table.
The models are User, Game and MarkedGame, where users can mark games as played. It's a many to many relationship:
User > MarkedGame < Game
What I want is a list of all games that haven't been marked by the user.
I know that I could do two separate queries and subtract them:
Game.all - current_user.games
But I don't like that this leaves me with an array rather than an Active Record relation object. Plus it seems like there should be a more performant way of doing it.
If there is no Active Record way of handling this, is there perhaps a SQL way? My raw SQL is not particularly strong so any help on that would be appreciated.
Thanks.
You can try the following:
Game.where.not(id: MarkedGame.where(user_id: current_user.id).pluck(:game_id))
That should do it. Returns all games that have not been marked by the current user.
Game.where('id not in (select game_id from marked_games where user_id = ?)', current_user.id)

SQL complicated query with joins

I have problem with one query.
Let me explain what I want:
For the sake of bravity let's say that I have three tables:
-Offers
-Ratings
-Users
Now what I want to do is to create SQL query:
I want Offers to be listed with all its fields and additional temporary column that IS NOT storred anywhere called AverageUserScore.
This AverageUserScore is product of grabbing all offers, belonging to particular user and then grabbing all ratings belonging to these offers and then evaluating those ratings average - this average score is AverageUserScore.
To explain it even further, I need this query for Ruby on Rails application. In the browser inside application you can see all offers of other users , with AverageUserScore at the very end, as the last column.
Associations:
Offer has many ratings
Offer belongs to user
Rating belongs to offer
User has many offers
Assumptions made:
You actually have a numeric column (of any type that SQL's AVG is fine with) in your Rating model. I'm using a column ratings.rating in my examples.
AverageUserScore is unconventional, so average_user_score is better.
You don't mind not getting users that have no offers: average rating is not clearly defined for them anyway.
You don't deviate from Rails' conventions far enough to have a primary key other than id.
Displaying offers for each user is a straightforward task: in a loop of #users.each do |user|, you can do user.offers.each do |offer| and be set. The only problem here is that it will execute a separate query for every user. Not good.
The "fetching offers" part is a standard N+1 counter seen even in the guides.
#users = User.includes(:offers).all
The interesting part here is only getting the averages.
For that I'm going to use Arel. It's already part of Rails, ActiveRecord is built on top of it, so you don't need to install anything extra.
You should be able to do a join like this:
User.joins(offers: :ratings)
And this won't get you anything interesting (apart from filtering users that have no offers). Inside though, you'll get a huge set of every rating joined with its corresponding offer and that offer's user. Since we're taking averages per-user we need to group by users.id, effectively making one entry per one users.id value. That is, one per user. A list of users, yes!
Let's stop for a second and make some assignments to make Arel-related code prettier. In fact, we only need two:
users = User.arel_table
ratings = Rating.arel_table
Okay. So. We need to get a list of users (all fields), and for each user fetch an average value seen on his offers' ratings' rating field. So let's compose these SQL expressions:
# users.*
user_fields = users[Arel.star] # Arel.star is a portable SQL "wildcard"
# AVG(ratings.rating) AS average_user_score
average_user_score = ratings[:rating].average.as('average_user_score')
All set. Ready for the final query:
User.includes(:offers) # N+1 counteraction
.joins(offers: :ratings) # dat join
.select(user_fields, average_user_score) # fields we need
.group(users[:id]) # grouping to only get one row per user

Search through Users with dynamic attributes with SQL

.Hi i'm working with Asp and SQL-Server and i have no problem with writing dynamic query
I'm trying to Write a search page for searching people.
I have 3 related tables:
See my table diagram in : http://tinypic.com/r/21159go/5
What i'm trying to do is to design a search page that a person can search users with a dynamic number of attributes.
Example:
think that a username called "User1" has 3 attributes named "Attr1", "Attr2" and "Attr3" related to him in "UserAttributes" table and "User2" has 3 attributes named "Attr1", "Attr2" and "Attr4".
Attribute names and other bunch of items unrelated to search function saved in "Attributes" Table. This is because i want to relate an attribute between multiple users. and their values are stored in "UserAttributes" table.
Well someone wants to search upon "Attr1" and "Attr2" and wants to return all users that have "Attr1" and "Attr2" with specific value.
I need a query to know how to implement this. I can write a dynamic query with asp.net so if someone please give me a query for this one example i have brought, i would be thankful
P.S. This is not my real database. my real database is much more complex and has more fields and tables but i just cut it and brought only necessary items. and because attributes are very dynamic they can't be embedded in table columns.
Thanks in advance
Based on your DB diagram your code would be something like this
update:
SELECT u.*
FROM users AS u
LEFT OUTER JOIN UserAttributes AS ua1
ON u.USER_ID = ua1.USER_ID
LEFT OUTER JOIN UserAttributes AS ua2
ON u.USER_ID = ua2.USER_ID
WHERE (
ua1.attribute_id = 'att1'
AND ua.attribute_value = 'MyValue' )
AND (
ua2.attribute_id = 'att2'
AND ua.attribute_value = 'MyValue2' )
In where clause you would specify the attirbute_Id and what value you are expecting out of it. Than just decide if you want to restrict users to have all values match or just one of them, in that case modify AND between statements to be OR
if you just want to do this quick and dirty you can create your own class library that can create adhoc sql that will pass to the database.
if you want more organized matter, create SP that will bring back users and accepts any left of id and value. Do a lot of that by passing list separated by comma, colon or semicolon. Than split it up in SP and filter results based on those values
there are many other alternatives like EntityFramework, LINQ-to-SQL and other options, just need to figure out what works best for you, how much time you want to spend on it and how easy will it be to support later.

How do I make a shopping list from multiple recipes in an SQL database?

I'm working on using a recipe database in SQLw, like the one in this question (which has helped a lot already) Structuring a recipe database , to combine the ingredients of several user selected recipes to a shopping list.
Also, the items on this shopping list are to be divided in two categories (eg: "groceries" and "check pantry")
Example case:
User can select 7 recipes to make a weekly mealplan in a form (almost got this part)
The given output is a shopping list of all the ingredients marked as "groceries" and a "check stock" list of all the ingredients marked as "pantry".
Any help at all would be much appreciated!
I had just posted a full solution, but given the subject here looks like it may be homework, I'm just going to point you in the right direction. If this isn't homework, leave a comment and I'll put the full solution back.
Since you have multiple recipes, a normal selection based on joins would give you back multiple rows per ingredient. You want some way to roll up all of the rows for a given ingredient into a single row and show a total of the quantity that you need.