first post here!
So as a young Padawan in SQL and Databases in general I am working on creating a database for a business to manage orders/items/prices etc. I want to create a View that from 3 linked tables {items,prices,discounts}(see tables at the bottom) calculates and show the total price of an item. As a rule, the discount column shouldn't be zero( because we only want in the table discounts entries with actual discount , not 0 )
From the following entries I want to show all of them but my view only shows the one's with discounts.
insert into items (`item_id`,`item_name`, `item_quantity`) values
(102,'item1',20),
(103,'item2',20),
(404,'item3',20); # <-- It won't be shown if I do SELECT * FROM view;
insert into discounts (`item_id`,`discount`) values
(102,50),
(103,25);
insert into prices (`item_id`,`price`) values
(102,100),
(103,100),
(404,100);
And here is my View:
CREATE VIEW ItemsPrice AS
SELECT
i.item_id,
i.item_name,
SUM((1-d.discount/100)*p.price*i.item_quantity)
FROM
items AS i
INNER JOIN
prices AS p ON i.item_id=p.item_id
INNER JOIN
discounts AS d ON (p.item_id=d.item_id)
GROUP BY item_id
ORDER BY total;
Here are my tables (just in case I made them wrong):
DROP TABLE IF EXISTS `items`;
CREATE TABLE `items` (
`item_id` int(30) NOT NULL,
`item_name` varchar(35) NOT NULL,
`item_quantity` double(25,0) ,
PRIMARY KEY (`item_id`)
);
#2=======
DROP TABLE IF EXISTS `prices`;
CREATE TABLE `prices` (
`item_id`int(30) NOT NULL,
`price` decimal(30,2) NOT NULL,
PRIMARY KEY (`item_id`),
CONSTRAINT `prices_ibfk_1` FOREIGN KEY (`item_id`) REFERENCES `items` (`item_id`)
);
#3=======
DROP TABLE IF EXISTS `discounts`;
CREATE TABLE `discounts` (
`item_id` int(30) NOT NULL,
`discount` int(3) NOT NULL,
PRIMARY KEY (`item_id`),
CONSTRAINT `discount_ibfk_1` FOREIGN KEY (`item_id`) REFERENCES `items` (`item_id`)
);
Sorry for not providing a schema. Don't know how to make one.
Hope I didn't waste much of your time! You are my hero.
You just need a left join - this will grab all items with prices even of they do not have a discount. You use COALESCE to replace the NULL discounts with a valid number.
SELECT
i.item_id,
i.item_name,
COALESCE(d.discount,0) as discount,
p.price,
i.item_quantity
FROM
items AS i
INNER JOIN
prices AS p ON i.item_id=p.item_id
LEFT JOIN
discounts AS d ON (p.item_id=d.item_id)
GROUP BY item_id
ORDER BY total;
Your query with COALESCE:
SELECT
i.item_id,
i.item_name,
(1-COALESCE(d.discount,0)/100)*p.price*i.item_quantity) as totalAmount
FROM
items AS i
INNER JOIN
prices AS p ON i.item_id=p.item_id
LEFT JOIN
discounts AS d ON (p.item_id=d.item_id)
GROUP BY item_id
ORDER BY total;
Related
I am fairly new with databases and I am starting with mysql.
I have 4 tables (movie, genre, movieGenre and movieRating):
movie:
CREATE TABLE `movie` (
`movieId` INT NOT NULL,
`title` VARCHAR(155) NOT NULL,
PRIMARY KEY (`movieId`)
);
genre
CREATE TABLE `genre` (
`code` INT NOT NULL AUTO_INCREMENT,
`genre` VARCHAR(20) NOT NULL,
PRIMARY KEY (`code`)
);
movieGenre
CREATE TABLE `movieGenre` (
`movieId` INT,
`genreId` INT,
CONSTRAINT `fk_movieGenre_movie` FOREIGN KEY (`movieId`) REFERENCES `movie`(`movieId`),
CONSTRAINT `fk_movieGenre_genre` FOREIGN KEY (`genreId`) references `genre`(`code`)
);
and movieRating
CREATE TABLE `movieRating` (
`userId` INT NOT NULL AUTO_INCREMENT,
`movieId` INT NOT NULL,
`rating` FLOAT NOT NULL,
`date` DATE,
CONSTRAINT `fk_movieRating_user` FOREIGN KEY (`userId`) REFERENCES `user`(`userId`),
CONSTRAINT `fk_movieRating_movie` FOREIGN KEY (`movieId`) REFERENCES `movie`(`movieId`)
);
I need to find the average rate for each movie genre, sorted in descended average rating value and if a genre does not have any associated rating, it should be reported with 0 ratings value
I am lost. I don't know how to achieve this result. Could you please help me?
I have figured out how to find the avg rate for each movie but I don't know how to change this so I find for each genre:
SELECT `movie`.`movieId`, AVG(`movieRating`.`rating`) FROM `movie`
INNER JOIN `movieRating` ON `movie`.`movieId` = `movieRating`.`movieId`
GROUP BY `movieRating`.`movieId`
ORDER BY AVG(`movieRating`.`rating`) DESC;
Well, I have put an ID in your genre table, otherwise I can't make this work. So, it has become:
CREATE TABLE `genre` (
`genreId` INT,
`code` INT NOT NULL AUTO_INCREMENT,
`genre` VARCHAR(20) NOT NULL,
PRIMARY KEY (`code`)
);
And I have to make the assumption that all the genres are defined in this table. I'll take this table as a base, and, as you suggested use a subquery:
SELECT
genre.code,
genre.genre,
(<my sub select comes here>)
FROM
genre;
This basically gets you a list of all genres. Now it is up to the subquery to give the average rate for the movies in each genre. That subquery could look something like this:
SELECT AVG(movieRating.rating)
FROM movieRating
JOIN movie ON movie.movieId = movieRating.movieId
JOIN movieGenre ON movieGenre.movieId = M.movieId
WHERE movieGenre.genreId = genre.genreId;
I kept it very simple. We start with the average we want, from movieRating, and work through the movie and movieGenre tables to get to the genreId in that last table. Notice the genre.genreId which comes from the main query. We are implicitly grouping by genreId.
Now you can put this subselect in the main query, but that still doesn't solve the situation in which there is not rating to take an average from. It would result in NULL, meaning: no result. That is almost good enough, but you could put a IFNULL() around it to get a proper zero result.
The total query would then become this:
SELECT
genre.code,
genre.genre,
IFNULL((SELECT AVG(movieRating.rating)
FROM movieRating
JOIN movie ON movie.movieId = movieRating.movieId
JOIN movieGenre ON movieGenre.movieId = M.movieId
WHERE movieGenre.genreId = genre.genreId), 0) AS Average
FROM
genre;
I can't guarantee this will work since I cannot test it, and testing is everything when writing queries.
You should left join all tables and then group by gerne
SELECT `genre`,AVG(IFNULL(`rating`,0)) avgrate
FROM `movie` m
LEFT JOIN `movieRating` mr ON m.`movieId` = mr.`movieId`
LEFT JOIN movieGenre mg ON mg.`movieId` = m.`movieId`
LEFT JOIN `genre` g ON g.`code` = mg.`genreId`
GROUP BY `genre`
I general produce data for your tables, and then start by joing the tables, and see f you get the result you want, if not change the joins to LET Join one by one till you get the result you want, of course you need ro calculate teh avg from 3 or 4 movies
There are employees in a company, each employee gets his salary each month. The employee can borrow some amount of money from his salary many times during a month.
It is required to display the employee name, his salary, the amount of money borrowed last time ,the sum of money borrowed and the remaining money from his salary at each transaction in a specific month.
These are my tables structures:
CREATE TABLE `employees`
(
`Employee_Id` Int NOT NULL AUTO_INCREMENT,
`Employeer_Name` Char(150) NOT NULL,
`Job` Varchar(100),
PRIMARY KEY (`Employee_Id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
ROW_FORMAT = Compact;
CREATE TABLE `Months`
(
`Month_Id` Int NOT NULL AUTO_INCREMENT,
`Month_Name` Varchar(100),
PRIMARY KEY (`Month_Id`),
UNIQUE `Month_Id` (`Month_Id`)
);
CREATE TABLE `Employees_Salary`
(
`Month_Id` Int NOT NULL,
`Employee_Id` Int NOT NULL,
`Salary` Decimal(10,2)
);
CREATE TABLE `salary_transaction`
(
`Salary_Transaction_Id` Int NOT NULL AUTO_INCREMENT,
`money_amount` Decimal(10,2) NOT NULL,
`salary_transaction_date` Date NOT NULL,
`Month_Id` Int,
`Employee_Id` Int,
PRIMARY KEY (`Salary_Transaction_Id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
ROW_FORMAT = Compact;
ALTER TABLE `Employees_Salary` ADD PRIMARY KEY (`Month_Id`,`Employee_Id`);
ALTER TABLE `Employees_Salary` ADD CONSTRAINT `Relationship41` FOREIGN KEY (`Month_Id`)
REFERENCES `Months` (`Month_Id`)
ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE `Employees_Salary` ADD CONSTRAINT `Relationship42` FOREIGN KEY (`Employee_Id`)
REFERENCES `employees` (`Employee_Id`)
ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE `salary_transaction` ADD CONSTRAINT `Relationship43` FOREIGN KEY (`Month_Id`, `Employee_Id`)
REFERENCES `Employees_Salary` (`Month_Id`, `Employee_Id`)
ON DELETE CASCADE ON UPDATE CASCADE;
I have tried this code but it subtract the last amount of money borrowed only from the salary
SELECT e.employee_id, e.employee_name, es.Month_Id, es.Salary, m.Month_Id, s.money_amount,
es.Salary-s.money_amount
FROM employees e
JOIN Employees_Salary es ON e.Employee_Id=es.Employee_Id
INNER JOIN Months m ON es.Month_Id=m.Month_Id
INNER JOIN salary_transaction s
WHERE es.Month_Id=1 AND es.Employee_Id=1
ORDER BY Salary_Transaction_Id DESC
LIMIT 1
All codes here in dbfiddle.uk site
First of all, you're missing ON after INNER JOIN salary_transaction s and since you specifically looking for WHERE es.Month_Id=1 AND es.Employee_Id=1 , I don't think ORDER BY Salary_Transaction_Id DESC and LIMIT 1 are needed. For the time being, let's remove that. What you need is actually a SUM() of total being borrowed and a GROUP BY. Something like this:
SELECT e.employee_id, e.employee_name, es.Month_Id, es.Salary, m.Month_Id,
SUM(s.money_amount) AS "Total Borrowed",
es.Salary-SUM(s.money_amount) AS "Salary Balance"
FROM employees e
JOIN Employees_Salary es ON e.Employee_Id=es.Employee_Id
INNER JOIN Months m ON es.Month_Id=m.Month_Id
INNER JOIN salary_transaction s ON e.Employee_Id=s.Employee_Id
WHERE es.Month_Id=1 AND es.Employee_Id=1
GROUP BY e.employee_id, e.employee_name, es.Month_Id, es.Salary, m.Month_Id;
Here's your updated fiddle : https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=3fbebdeccf3bc910339dd143e3bcf3c7
I have a database that contains products, beds to be specific. The details of each bed is stored in a products table, but then I have a separate table, product_bedding_sizes that holds records for each size a bed can in. So one bed may have five entries in this table (it’s a one-to-many relationship).
That bit is done. However, on the public website, visitors can search products and restrict their search to a particular bed size. So my question is, how can I take an array of sizes and select all bed products that may have one or more entries for the specified sizes?
A simplified schema:
CREATE TABLE `products` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB;
CREATE TABLE `product_bedding_sizes` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`product_id` int(10) unsigned NOT NULL,
`size` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `product_id` (`product_id`)
) ENGINE=InnoDB;
ALTER TABLE `product_bedding_sizes`
ADD CONSTRAINT `product_bedding_sizes_ibfk_1`
FOREIGN KEY (`product_id`)
REFERENCES `products` (`id`)
ON DELETE CASCADE
ON UPDATE CASCADE;
If the user selects say, 90cm and 120cm, the plain English explanation of the query would be:
Select all from the products table where it may have a sizein the product_bedding_sizes table that’s 90cm or 120cm.
I take your requirement to be that a row should be returned if the product has a size of 90 OR if it has a size of 120, or both. (The query would be slightly different to return rows where the product has both size of 90 AND a size of 120.)
Any of these queries return the specified result:
SELECT p.id
, p.name
FROM products p
JOIN product_bedding_sizes s
ON s.product_id = p.id
AND s.size IN (90,120)
GROUP
BY p.id
, p.name
--or--
SELECT p.id
, p.name
FROM products p
JOIN ( SELECT s.product_id
FROM product_bedding_sizes s
WHERE s.size IN (90,120)
GROUP BY s.product_id
) t
ON t.product_id = p.id
ORDER
BY p.id
, p.name
-- or --
SELECT p.id
, p.name
FROM products p
WHERE EXISTS
( SELECT 1
FROM product_beddings_sizes s
WHERE s.product_id = p.id
AND s.size IN (90,120)
)
ORDER
BY p.id
, p.name
-- or --
SELECT p.id
, p.name
FROM products p
WHERE p.id IN
( SELECT s.product_id
FROM product_bedding_sizes s
WHERE s.size IN (90,120)
)
ORDER
BY p.id
, p.name
I have 2 tables, items and members :
CREATE TABLE IF NOT EXISTS `items` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`member` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `members` (
`id` int(5) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
What if, for example I have a record inside items, such as
INSERT INTO `test`.`items` (
`id` ,
`name` ,
`member`
)
VALUES (
NULL , 'xxxx', '1, 2, 3'
);
in members :
INSERT INTO `members` (`id`, `name`) VALUES
(1, 'asdf'),
(2, 'qwert'),
(3, 'uiop'),
(4, 'jkl;');
and I'd like to display items.member data with members.name, something like 1#asdf, 2#qwert, 3#uiop??
I've tried the following query,
SELECT items.id, items.name, GROUP_CONCAT(CONCAT_WS('#', members.id, members.name) ) as member
FROM `items`
LEFT JOIN members AS members on (members.id = items.member)
WHERE items.id = 1
But the result is not like I expected. Is there any other way to display the data via one call query? Because I'm using PHP, right now, i'm explode items.member and loop it one by one, to display the members.name.
You could look into using FIND_IN_SET() in your join criteria:
FROM items JOIN members ON FIND_IN_SET(members.id, items.member)
However, note from the definition of FIND_IN_SET():
A string list is a string composed of substrings separated by “,” characters.
Therefore the items.member column should not contain any spaces (I suppose you could use FIND_IN_SET(members.id, REPLACE(items.member, ' ', '')) - but this is going to be extremely costly as your database grows).
Really, you should normalise your schema:
CREATE TABLE memberItems (
item_id INT(5) NOT NULL,
member_id INT(5) NOT NULL,
FOREIGN KEY item_id REFERENCES items (id),
FOREIGN KEY member_id REFERENCES members (id)
);
INSERT INTO memberItems
(item_id, member_id)
SELECT items.id, members.id
FROM items
JOIN members ON FIND_IN_SET(members.id, REPLACE(items.member,' ',''))
;
ALTER TABLE items DROP member;
This is both index-friendly (and therefore can be queried very efficiently) and has the database enforce referential integrity.
Then you can do:
FROM items JOIN memberItems ON memberItems.item_id = items.id
JOIN members ON members.id = memberItems.member_id
Note also that it's generally unwise to use GROUP_CONCAT() to combine separate records into a string in this fashion: your application should instead be prepared to loop over the resultset to fetch each member.
Please take a look at this sample:
SQLFIDDLE
Your query seems to work for what you have mentioned in the question... :)
SELECT I.ID, I.ITEM,
GROUP_CONCAT(CONCAT("#",M.ID,
M.NAME, " ")) AS MEMB
FROM ITEMS AS I
LEFT JOIN MEMBERS AS M
ON M.ID = I.MID
WHERE i.id = 1
;
EDITTED ANSWER
This query will not work for you¬ as your schema doesn't seem to have any integrity... or proper references. Plus your memeber IDs are delimtted by a comma, which has been neglected in this answer.
I have two tables:
product_description( name , product_id)
product( quantity, stock_status_id, price, product_id)
What I am trying to do is run a query that will get me the above data but I am not sure how to implement a join to get the joined data from both tables.
Resolved
I did the following:
SELECT product_description.name, product.quantity,product.price
FROM product
INNER JOIN product_description
ON product.product_id=product_description.product_id
ORDER BY product_description.name
Operating under the assumption that you have matching product_ids in each table, here's a query that will return the data you need using implicit joins:
SELECT product.product_id, name, quantity, stock_status_id, price
FROM product, product_description
WHERE product.product_id = product_description.product_id
UPDATE:
This works as I would expect. Here are two table dumps that might help you, and the output of the query.
product table:
--
-- Table structure for table `product`
--
CREATE TABLE IF NOT EXISTS `product` (
`product_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(256) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`product_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=3 ;
--
-- Dumping data for table `product`
--
INSERT INTO `product` (`product_id`, `name`) VALUES
(1, 'Croissant'),
(2, 'Danish');
product_description table:
--
-- Table structure for table `product_description`
--
CREATE TABLE IF NOT EXISTS `product_description` (
`product_id` int(11) NOT NULL,
`quantity` int(11) NOT NULL,
`stock_status_id` int(11) NOT NULL,
`price` double NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Dumping data for table `product_description`
--
INSERT INTO `product_description` (`product_id`, `quantity`, `stock_status_id`, `price`) VALUES
(1, 6, 2, 12.5),
(2, 13, 1, 19.25);
Output from the above query:
"1","Croissant","6","2","12.5"
"2","Danish","13","1","19.25"
maybe something like this?
select p.id, p.quantity, p.stock_status_id, p.price, d.name
from Product p inner join `Product Description` d on d.product_id=p.id
although I'm still not sure of what the table schema actually looks like from the description in the question.
SELECT
-- name each column you want here
-- prefix columns from the PRODUCT table with its alias like so:
p.product_id,
-- prefix columns from the other table as its alias
d.quantity
FROM
PRODUCT p -- name of your PRODUCT table aliased as p
INNER JOIN -- join against your other table
PRODUCT_DESCRIPTION d -- name of your other table aliased as d
ON
p.product_id = d.product_id -- match the foreign keys
See INNER JOIN documentation for more details.
NOTE: This won't work as-is, you'll need to provide the columns that you want from each table in the SELECT clause, and probably fix the table names to match for your application.