Retrieving data from a many-to-many relationship in a recipes database - sql

I am attempting to create a recipes database where a user can input ingredients and it will output a list of potential recipes. I have created three tables:
CREATE TABLE [dbo].[Ingredients] (
[Ingredient_ID] INT IDENTITY (1, 1) NOT NULL,
[Name] VARCHAR (50) NOT NULL,
PRIMARY KEY CLUSTERED ([Ingredient_ID] ASC)
);
CREATE TABLE [dbo].[recipes] (
[Recipe_ID] INT IDENTITY (1, 1) NOT NULL,
[Name] VARCHAR (50) NOT NULL,
[Instructions] TEXT NULL,
[Preperation_Time] FLOAT (53) NULL,
[Author] VARCHAR (50) NULL,
CONSTRAINT [PK.recipes] PRIMARY KEY CLUSTERED ([Recipe_ID] ASC)
);
CREATE TABLE [dbo].[RecipeIngredients] (
[Recipe_ID] INT NOT NULL,
[Ingredient_ID] INT NOT NULL,
PRIMARY KEY CLUSTERED ([Recipe_ID] ASC, [Ingredient_ID] ASC),
CONSTRAINT [FK_RecipeIngredients_To_Ingredients] FOREIGN KEY ([Ingredient_ID]) REFERENCES [dbo].[Ingredients] ([Ingredient_ID]),
CONSTRAINT [FK_RecipeIngredients_To_Recipes] FOREIGN KEY ([Recipe_ID]) REFERENCES [dbo].[recipes] ([Recipe_ID])
);
I have populated all tables and I am now attempting to retrieve the recipes based on what the user has entered.
I have a created a test SQL statement to retrieve all recipes that contain 'Eggs' using:
string sqlString = "SELECT recipes.Name, Instructions, recipes.Preperation_Time, Author FROM RecipeIngredients" +
" INNER JOIN recipes ON recipes.Recipe_ID = RecipeIngredients.Recipe_ID" +
" INNER JOIN Ingredients ON Ingredients.Ingredient_ID = RecipeIngredients.Ingredient_ID" +
" WHERE ingredients.Name = 'Eggs'";
The data does not show up in my dataGridView, but I am unsure if it is because the statement is wrong or other factors.
Is the statement correct? I am unfamiliar with the INNER JOIN command.
I am also unsure how to design an Sql statement that can take a varying amount of ingredient names without creating an Sql statement for every possibility.
Thanks in advance, if you need me to expand on anything I have asked please ask.

Here is the query that should work. Use of aliases is recommended
SELECT r.Name, r.Instructions, r.Preperation_Time, r.Author
FROM Ingredients i
join RecipeIngredients ri on i.Ingredient_ID = ri.Ingredient_ID
join recipes r on ri.Recipe_ID = r.Recipe_ID
where i.Name = 'Eggs'
You may want to run it through SQL Server Management Studio to ascertain return of result before coding it in C# solution.

Related

In a SELECT command, how do I use data from one table to specify data in another?

I have 2 tables. What is important is the PlayerId and the Username.
CREATE TABLE [dbo].[Run]
(
[RunId] INT NOT NULL,
[PlayerId] INT NOT NULL,
[Duration] TIME(7) NOT NULL,
[DateUploaded] NCHAR(10) NOT NULL,
[VersionId] INT NOT NULL,
PRIMARY KEY CLUSTERED ([RunId] ASC),
CONSTRAINT [FK_Run_Player]
FOREIGN KEY ([PlayerId]) REFERENCES [dbo].[Player] ([PlayerId]),
CONSTRAINT [FK_Run_Version]
FOREIGN KEY ([VersionId]) REFERENCES [dbo].[Version] ([VersionId])
);
CREATE TABLE [dbo].[Player]
(
[PlayerId] INT NOT NULL,
[Username] NCHAR(20) NOT NULL,
[ProfilePicture] IMAGE NULL,
[Country] NCHAR(20) NOT NULL,
[LeagueId] INT NULL,
[DateJoined] DATE NULL,
PRIMARY KEY CLUSTERED ([PlayerId] ASC),
CONSTRAINT [FK_Player_League]
FOREIGN KEY ([LeagueId]) REFERENCES [dbo].[League] ([LeagueId])
);
I have a select command:
SELECT
PlayerId, Duration, VersionId, DateUploaded
FROM
[Run]
(with apologies in advance for my messy made up pseudocode), what I need it to do is:
SELECT (Player.PlayerId.Username)
What I basically need it to do, is instead of giving me just PlayerId, I need it to get the corresponding Username (from the other table) that matches each PlayerId (PlayerId is a foreign key)
So say for example instead of returning
1, 2, 3, 4, 5
it should return
John12, Abby2003, amy_932, asha7494, luke_ww
assuming, for example, Abby2003's PlayerId was 2.
I've done trial and error and either nobody's tried this before or I'm searching the wrong keywords. This is using VS 2022, ASP.NET Web Forms, and Visual Basic, but that shouldn't affect anything I don't think. Any syntax ideas or help would be greatly appreciated.
try this for join the 2 Table togother
SELECT R.RunId
,R.PlayerId
,R.Duration
,R.DateUploaded
,R.VersionId
,P.Username
,P.ProfilePicture
,P.Country
,P.LeagueId
,P.DateJoined
FROM Run R
inner join Player P on R.PlayerId = P.PlayerId
Usually in this case joins are used. You can join the two tables together, give them aliases (or don't, personal preference really), then select what you need. In this case, you would probably want an inner join. Your query would probably look something like this:
SELECT p.Username FROM [Run] r
INNER JOIN [Player] p ON r.PlayerId = p.PlayerId
Then if you need to you can put a WHERE clause after that.
More about joins here

Populating new table with values from another table based on foreign key relationship

I have a SQL Server database connected to QGIS. I am using SQL Server Management Studio, trying to fill in columns in a new table with values from another table. I know that it is better practice to create a view, but when I attempt to load a view into QGIS, it returns the following error message:
Layer is not valid: The layer dbname='MBA-LBA_Ornaments'
host=DESKTOP-DLA7UV8 estimatedmetadata=true
srid=4326 type=Point disableInvalidGeometryHandling='1'
primaryKeyInGeometryColumns='0' table="dbo".
"GoldWeight" (Geo4) is not a valid layer and can not be added to the map.
Reason: No primary key could be found on table GoldWeight
The only workaround I can come up with is to create a new table incorporating fields from the Ornaments table and the Sites table. Here is the structure of the relevant tables:
CREATE TABLE [dbo].[Ornaments](
[OrnID] [smallint] IDENTITY(1000,1) NOT NULL,
[OrnForm] [varchar](20),
[OrnSubtype] [varchar](20),
[FragmentYN] [char](1),
[Material] [varchar](30),
[Period] [varchar](10),
[Phase] [varchar](20),
[BasisOfDate] [varchar](50),
[SiteID] [smallint],
[DiscContext] [varchar](20),
[DiscYear] [varchar](10),
[SingleFindYN] [char](1),
[BrokenYN] [varchar](2),
[DistortedYN] [char](1),
[WeightG] [numeric](8, 2),
[SpatialPrecisionRating] [char](1),
[ImageYN] [char](1),
[Comments] [varchar](500),
CREATE TABLE [dbo].[Sites](
[SiteID] [smallint] IDENTITY(1,1) NOT NULL,
[SiteName] [varchar](50),
[SiteType] [varchar](50),
[Region] [varchar](50),
[CountyDistrict] [nvarchar](30),
[Latitude] [decimal](11, 6),
[Longitude] [decimal](11, 6),
[Comments] [varchar](500),
[Geo4] AS (case when [Latitude] IS NOT NULL AND [Longitude] IS NOT NULL
then [geography]::Point([Latitude],[Longitude],(4326)) end),
The GoldWeight table was created using:
SELECT SiteID, SUM(WeightG) AS 'Weight'
INTO GoldWeight
FROM Ornaments
WHERE Material = 'gold'
GROUP BY SiteID
I then added columns SiteName and Geo4 using:
ALTER TABLE GoldWeight
ADD SiteName VARCHAR(50) NULL,
Geo4 geography NULL
and added a foreign key using:
ALTER TABLE GoldWeight
ADD FOREIGN KEY (SiteID) REFERENCES Sites(SiteID)
I thought adding a foreign key would populate the fields, but of course that is not true. Is there a way to populate the SiteName and Geo4 fields with the appropriate data based on the SiteID foreign key relationship?
This query should serve the purpose, however copying the data to the referencing table is not a good practice.
UPDATE G
SET G.SiteName = S.SiteName,
G.Geo4 = S.Geo4
FROM GoldWeight G
INNER JOIN SITES S ON S.SiteID= G.SiteID
I ended up figuring out an answer, which is to select the results from a view into a new table using:
SELECT dbo.GoldWeight.SiteID,
dbo.GoldWeight.Weight,
dbo.Sites.SiteName,
dbo.Sites.SiteType,
dbo.Sites.Region,
dbo.Sites.CountyDistrict,
dbo.Sites.Geo4
INTO GoldWeight2
FROM dbo.GoldWeight INNER JOIN
dbo.Sites ON dbo.GoldWeight.SiteID = dbo.Sites.SiteID
then adding a foreign key:
ALTER TABLE GoldWeight2
ADD FOREIGN KEY (SiteID) REFERENCES Sites(SiteID)
then setting the SiteID column to NOT NULL:
ALTER TABLE
QGIS_GoldWeight2
ALTER COLUMN
SiteID smallint NOT NULL;
then setting SiteID as primary key:
ALTER TABLE QGIS_GoldWeight2
ADD PRIMARY KEY (SiteID);
I had to uncheck the box "Prevent saving changes that require the table re-creation" under Tools > Options > Designer to successfully set the primary key.
It creates a table that QGIS can read, but the table will have to be deleted after import into QGIS because the data does not update if fields in the Sites table change.

How do I make the columns of one table not contain the columns of another table

I have a database with these two tables:
CREATE TABLE Photos(
photoId INT NOT NULL AUTO_INCREMENT,
userId INT NOT NULL,
url VARCHAR(200) NOT NULL UNIQUE,
uploadDate DATE NOT NULL,
title VARCHAR(80) NOT NULL,
description VARCHAR(400),
visibility ENUM ('Pública', 'Privada') NOT NULL,
PRIMARY KEY (photoId),
FOREIGN KEY (userId) REFERENCES Users (userId) ON DELETE CASCADE
);
CREATE TABLE InappropiateWords(
inappropiateWordId INT NOT NULL AUTO_INCREMENT,
word VARCHAR(80),
PRIMARY KEY (inappropiateWordId)
);
I'm asked to check that the title and/or description of a photo doesn't contain any inappropiate word. I guess I need to create a trigger but I don't know how to do it. Any help is appreciated. Thanks!
This is not a requirement that you can implement at the database level.
If you are really looking to ensure that the "description" or "title" does not contain inappropriate word, then
"What is Inappropriate" has to be defined?. This is step 1. You have a table (table 2) which I assume will store all inappropriate words.
Then when the program that inserts the picture and description/title is invoked, the code needs to take the title and description and parse the words and compare them against the "inappropriate_word" table and then decide which action to take.
The description or title might have a string of words in which case you may have to parse each word and check against the table(2).
This is not a take away solution but at least I hope this helps.
You can create a table variable that loads on page and perform a join to find those values.
CREATE TABLE #tbl_LinkedNames(
Name varchar(50)
, AssociatedNameNbr varchar(50)
, userId int
, inappropiateWordId int
)
INSERT INTO #tbl_LinkedNames(
NameNbr, AssociatedName, userId, inappropiateWordId )
VALUES
('A0001', 'badword', 1, 4),
('A0002', 'wORSEWORD', 2, 5),
('A0002', 'BADW00rds', 3, 6),
('A1001', 'badw', 4, 1),
('A2002', 'lengua', 5, 2),
('A3002', 'diferente', 6, 3)
SELECT * FROM #tbl_LinkedNames
From here it is a simple join based off the called stored procedure.
SELECT
*
FROM
Photos AS p
LEFT JOIN #tbl_LinkedNames AS t_LN ON
p.userId = t_LN.userID
AND
p.inappropiateWordId = t_LN.inappropiateWordId
LEFT JOIN InappropiateWords AS Ip ON
Ip.inappropiateWordId = t_LN.inappropiateWordId

How to optimize SQL query that uses GROUP BY and joined many-to-many relation tables?

I have tables with many-to-many relations:
CREATE TABLE `item` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL DEFAULT '',
`size_id` tinyint(3) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
INDEX `size` (`size_id`)
);
CREATE TABLE `items_styles` (
`style_id` smallint(5) unsigned NOT NULL,
`item_id` mediumint(8) unsigned NOT NULL,
PRIMARY KEY (`item_id`, `style_id`),
INDEX `style` (`style_id`),
INDEX `item` (`item_id`),
CONSTRAINT `items_styles_item_id_item_id` FOREIGN KEY (`item_id`) REFERENCES `item` (`id`)
);
CREATE TABLE `items_themes` (
`theme_id` tinyint(3) unsigned NOT NULL,
`item_id` mediumint(8) unsigned NOT NULL,
PRIMARY KEY (`item_id`, `theme_id`),
INDEX `theme` (`theme_id`),
INDEX `item` (`item_id`),
CONSTRAINT `items_themes_item_id_item_id` FOREIGN KEY (`item_id`) REFERENCES `item` (`id`)
);
I'm trying to get the report that shows style_id and the number of items that use this style but with applying filters to the item table and/or to another table, like this:
SELECT i_s.style_id, COUNT(i.id) total FROM item i
JOIN items_themes i_t ON i.id = i_t.item_id AND i_t.theme_id IN (6, 7)
JOIN items_styles i_s ON i.id = i_s.item_id
GROUP BY i_s.style_id;
-- or like this
SELECT i_s.style_id, COUNT(i.id) total FROM item i
JOIN items_themes i_t ON i.id = i_t.item_id AND i_t.theme_id IN (6, 7)
JOIN items_styles i_s ON i.id = i_s.item_id
WHERE i.size_id != 3
GROUP BY i_s.style_id;
The problem is that tables are pretty big so queries take a long time to execute (~8 seconds)
item - 8M rows
items_styles - 12M rows
items_themes - 11M rows
Is there any way to optimize these queries? If not, what approach can be used to receive such reports.
I will be grateful for any help. Thanks.
First, you don't need the items table for the queries. Probably doesn't have much impact on performance, but no need.
So you can write the query as:
SELECT i_s.style_id, COUNT(*) as total
FROM items_themes i_t JOIN
items_styles i_s
ON i_s.item_id = i_t.item_id
WHERE i_t.theme_id IN (6, 7)
GROUP BY i_s.style_id;
For this query, you want an index on items_themes(theme_id, item_id). There is no much you can do about the GROUP BY.
Then, I don't think this is what you really want, because it will double count an item that has both themes. So, use EXISTS instead:
SELECT i_s.style_id, COUNT(*) as total
FROM items_styles i_s
WHERE EXISTS (SELECT
FROM items_themes i_t
WHERE i_t.item_id = i_s.item_id AND
i_t.theme_id IN (6, 7)
)
GROUP BY i_s.style_id;
For this, you want an index on items_themes(item_id, theme_id). You can also try an index on items_styles(style_id). Some databases might be able to use that one, but I am guessing not MariaDB.
In a many-to-many table, it is optimal to have these two indexes:
PRIMARY KEY (`item_id`, `style_id`),
INDEX `style` (`style_id`, `item_id`)
And be sure to use InnoDB.
More discussion: http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table
Still, you have two many-to-many mappings, so there probably is no great solution.

Inner join between different database

I want to create a table using the following script in a database called DeltaDatabase:
CREATE TABLE [dbo].[OutStatus](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[OutId] [int] NOT NULL,
[StatusType] [varchar](255) NULL,
[StatusDate] [datetime] NULL)
I would then like to INNER JOIN a column into this table from another database called CoreDatabase.
The column name is sourceId from the table Client. So in other words OutId needs to be foreign key of SourceId.
How do I join that column into my OutStatus table from the other database using the create table script?
The basic syntax to retrieve data would be:
SELECT *
FROM CoreDatabase.dbo.Client c
INNER JOIN DeltaDatabase.dbo.OutStatus os ON c.SourceId = os.OutId
You need to fully qualify the tables name with: DatabaseName.Schema.TableName
You may wish to limit the columns or add a where clause to reduce the data that is returned.
As far as creating a foreign key across databases goes, it's not something you can do. You would have to use triggers or some other logic to maintain referential integrity between the primary and foreign keys.
Try the below query
Select * from DeltaDatabase.dbo.OutStatus OUS
Inner Join CoreDatabase.dbo.Client CL on OUS.OutId=CL.sourceId