View help. Pulling data from 3 tables - mysql

Okay, I need some major help with this subject. This is what I need the view to do. It needs to take Sum of the DKP_Change Column in the Attendance table
SELECT SUM(a.DKP_Change) FROM Attendance AS a GROUP BY Name
add the value of the initial DKP from the characters table
SELECT b.Inital_DKP FROM Characters AS b GROUP BY Name
Subtract the sum of the raid drops tabe cost
SELECT SUM(c.Cost) FROM Raid_Drops AS c GROUP BY Name
I'm entirely new to the idea of VIEWS and i'm not sure where to begin with, the name of the view should be DKP, the columns should be Name and Total_DKP, where total dkp is calculated from teh above select statements.
Here are the creates for all 3 tables.
CREATE TABLE `Attendance` (
`Date` date NOT NULL,
`Name` varchar(20) NOT NULL,
`Hours` int(11) NOT NULL,
`Penalty` float NOT NULL,
`Rank` set('Raider','Core','Elite') NOT NULL,
`Rate` int(11) NOT NULL,
`DKP_Change` float NOT NULL,
`RecordNumber` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`RecordNumber`)
) ENGINE=MyISAM AUTO_INCREMENT=15 DEFAULT CHARSET=latin1
CREATE TABLE `Characters` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(25) NOT NULL,
`Class` varchar(25) NOT NULL,
`Spec` varchar(25) NOT NULL,
`Position` set('Healer','Tank','DPS') NOT NULL COMMENT 'Healer, Tank, or DPS',
`Usable` set('Cloth','Mail','Plate') NOT NULL COMMENT 'Type of Usable Armor? Cloth, Mail, Or Plate',
`Primary Stat` set('Agility','Strength','Intellect','Healer','Tank') NOT NULL COMMENT 'Used for Sorting Only(ie dps trinket with agility strength dps not eligible)',
`Initial_DKP` int(11) NOT NULL COMMENT 'DKP given at the start of current tier.',
`Total_DKP` int(11) NOT NULL COMMENT 'Huge Complicated Mess.',
PRIMARY KEY (`ID`)
) ENGINE=MyISAM AUTO_INCREMENT=2 DEFAULT CHARSET=latin1
CREATE TABLE `Raid_Drops` (
`Record Number` int(11) NOT NULL,
`Date` date NOT NULL,
`Name of Item` varchar(25) NOT NULL,
`Item Slot` enum('Main Hand','Off Hand','Head','Neck','Shoulder','Back','Chest','Wrist','Hands','Waist','Legs','Feet','Ring 1','Ring 2','Trinket 1','Trinket 2') NOT NULL,
`Player_Name` varchar(25) NOT NULL,
`Cost` float NOT NULL,
PRIMARY KEY (`Record Number`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

You could
Join the three tables in a subselect, grouping on Name
Perform your calculations on the results of each subselect
The only part that's not entirely clear to me is if the names in Characters are unique or not. If they are, you can drop the group by. If they are not, the AVG might give you unexpected results.
SQL Statement
SELECT sumA
, initialB
, sumC
, sumA + initialB - sumC
, a.Name
FROM (
SELECT Name, SUM(DKP_Change) AS sumA
FROM Attendance
GROUP BY Name
) AS a
INNER JOIN (
SELECT Name, Inital_DKP AS initialB
FROM Characters
) AS b ON a.Name = b.Name
INNER JOIN (
SELECT Name, SUM(Cost) AS sumC
FROM Raid_Drops
GROUP BY Name
) AS c ON c.Name = b.Name

Related

How to perform IS_THE_SUBSET_OF function in MySQL?

Added: the CREATE code for student and takes
CREATE TABLE `student` (
`sid` VARCHAR(6) NOT NULL,
`sname` VARCHAR(6) NULL DEFAULT NULL,
`sex` VARCHAR(2) NULL DEFAULT NULL,
`age` TINYINT(3) UNSIGNED NULL DEFAULT NULL,
`dept` VARCHAR(50) NULL DEFAULT NULL,
`class` VARCHAR(4) NULL DEFAULT NULL,
PRIMARY KEY (`sid`)
);
CREATE TABLE `takes` (
`sid` VARCHAR(6) NOT NULL,
`cid` VARCHAR(3) NOT NULL,
`score` TINYINT(3) UNSIGNED NULL DEFAULT NULL,
PRIMARY KEY (`sid`, `cid`)
)
I am going to display the IDs and names of all students who have taken the courses which are taken by the student whose ID is '31401'. I have the following code:
SELECT sid, sname
FROM student S
WHERE NOT EXISTS((SELECT cid
FROM takes
WHERE sid = '31401')
EXCEPT
(SELECT cid
FROM takes T
WHERE S.sid = T.sid));
However, there is no EXCEPT operation in MySQL. So I wonder if I could rewrite like this:
SELECT sid, sname
FROM student S
WHERE ((SELECT cid
FROM takes
WHERE sid = '31401')
IS_THE_SUBSET_OF
(SELECT cid
FROM takes T
WHERE S.sid = T.sid));
How can I implement the IS_THE_SUBSET_OF function?
There is no set-based operator that does what you want. You can use join, group by and some other logic:
select t.sid
from takes t join
takes ts
on t.cid = ts.cid and ts.sid = 31401 -- don't use single quotes for a numeric constant
group by t.sid
having count(*) = (select count(*) from takes ts2 where ts2.sid = 31401);
This formulation assumes that takes does not have duplicate sid/cid pairs.

MySQL returning all matches from a table and indicating if an id is on another table

How can I return, on a select, a field that indicates that an id was found?
My goal is to return all songs(song) from a specific source(source) checking if an user(user) has it or not (user_song).
The query I made almost works. If I remove 'hasSong' (which Im trying to indicate that an user has a song or not), I can see all songs.
If I keep 'hasSong', I see all songs repeating the song for each user.
QUERY:
SELECT DISTINCT(song.id) AS id_song, CONCAT(song.article, ' ', song.name) AS name
FROM `song`
LEFT JOIN `user_song` ON `song`.`id` = `user_song`.`id_song`
LEFT JOIN `user` ON `user`.`id` = `user_song`.`id_user`
JOIN `song_source` ON `song`.`id` = `song_source`.`id_song`
WHERE `song_source`.`id_source` = '1'
AND ( `user_song`.`id_user` = '3' OR song.id = song_source.id_song )
ORDER BY `song`.`name` ASC
DB:
CREATE TABLE `song` (
`id` int(11) NOT NULL,
`article` varchar(10) NOT NULL,
`name` varchar(150) NOT NULL,
`shortname` varchar(150) NOT NULL,
`year` int(11) NOT NULL,
`artist` int(11) NOT NULL,
`duration` int(11) NOT NULL,
`genre` int(11) NOT NULL,
`updated` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `song_source` (
`id_song` int(11) NOT NULL,
`id_source` int(11) NOT NULL
)
CREATE TABLE `source` (
`id` int(11) NOT NULL,
`article` varchar(10) NOT NULL,
`name` varchar(150) NOT NULL,
`updated` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user` (
`id` int(11) NOT NULL,
`email` varchar(100) NOT NULL,
`password` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `user_song` (
`id_user` int(11) NOT NULL,
`id_song` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
The specification isn't entirely clear, ...
To return all songs (with no repeated values of song.id) that are from a particular source (id_source='1'),
along with an indicator, a value of 0 or 1, that tells us if there's a row in user_song that matches on id_song and is related to a particular user,(id_user = '3')
something like this:
SELECT s.id AS id_song
, MAX( CONCAT(s.article,' ',s.name) ) AS name
, MAX( IF(us.id_user = '3' ,1,0) ) AS has_song
FROM `song` s
JOIN `song_source` ss
ON ss.id_song = s.id
AND ss.id_source = '1'
LEFT
JOIN `user_song` us
ON us.id_song = s.id
AND us.id_user = '3'
GROUP BY s.id
ORDER BY MAX(s.name)
There are a couple of other query patterns that will return an equivalent result. For example, we could use a correlated subquery in the SELECT list.
SELECT s.id AS id_song
, MAX( CONCAT(s.article,' ',s.name) ) AS name
, ( SELECT IF( COUNT(us.id_user) >0,1,0)
FROM `user_song` us
WHERE us.id_song = s.id
AND us.id_user = '3'
) AS has_song
FROM `song` s
JOIN `song_source` ss
ON ss.id_song = s.id
AND ss.id_source = '1'
GROUP BY s.id
ORDER BY MAX(s.name)
These queries are complicated by the fact that there are no guarantees of uniqueness in any of the tables. If we had guarantees, we could eliminate the need for a GROUP BY and aggregate functions.
Please consider adding PRIMARY and/or UNIQUE KEY constraints on the tables, to prevent duplication. The way the tables are defined, we could add multiple rows to song with the same id value. (And those could have different name values.)
(And the queries would be much simpler if we had some guarantees of uniqueness.)

Between a MIN and MAX date in joined table

I have a table called Booking and a table called FacilityBooking. A booking is a composition of facility bookings, a one to many relation. The date and time of the booking is determined by the lowest start date, and the highest end date of the facility bookings that belongs to it.
I want to pull some statistics of how many private and how many business bookings there has been between two dates.
CREATE TABLE `Booking` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`comments` varchar(255) DEFAULT NULL,
`createdBy` varchar(255) DEFAULT NULL,
`customerName` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`isPaid` bit(1) DEFAULT NULL,
`isPrivateClient` bit(1) DEFAULT NULL,
`needsPermission` bit(1) DEFAULT NULL,
`phoneNumber` varchar(255) DEFAULT NULL,
`referenceNumber` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
# Dump of table FacilityBooking
# ------------------------------------------------------------
CREATE TABLE `FacilityBooking` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`comments` varchar(2000) DEFAULT NULL,
`from` datetime DEFAULT NULL,
`to` datetime DEFAULT NULL,
`bookablePlace_id` int(11) DEFAULT NULL,
`booking_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_2tv9w7g5vyx9po8vs6ceogldb` (`bookablePlace_id`),
KEY `FK_n17h188ecbdos5lsva51b8j29` (`booking_id`),
CONSTRAINT `FK_n17h188ecbdos5lsva51b8j29` FOREIGN KEY (`booking_id`) REFERENCES `Booking` (`id`),
CONSTRAINT `FK_2tv9w7g5vyx9po8vs6ceogldb` FOREIGN KEY (`bookablePlace_id`) REFERENCES `BookablePlace` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
I have created an sqlfiddle: http://sqlfiddle.com/#!9/7ae95/2
And this is what i have so far:
SELECT
CASE isPrivateClient
WHEN 0 THEN "business"
WHEN 1 THEN "private"
END AS clientType,
count(isPrivateClient) as count
FROM
Booking
GROUP BY
isPrivateClient
So what i need from here is to join the facilitybookings and search between the lowest from date and the highest to date.
Hope someone can help me :)
Join the FacilityBooking table and filter using WHERE:
SELECT
CASE isPrivateClient
WHEN 0 THEN "business"
WHEN 1 THEN "private"
END AS clientType,
count(FacilityBooking.id) as count
FROM
Booking INNER JOIN FacilityBooking ON
Booking.id = FacilityBooking.booking_id
-- Between 2015-05-01 AND 2015-06-01 INCLUSSIVE'
WHERE FacilityBooking.from <= '2015-06-01' AND FacilityBooking.to >= '2015-05-01'
GROUP BY
isPrivateClient
fixed fiddle
If you want to only include "full" Booking in which all FacilityBooking are between 2 date, something like this should do the trick :
SELECT clientType, count(bookId)
FROM (
SELECT
b.id as bookId,
CASE b.isPrivateClient
WHEN 0 THEN "business"
WHEN 1 THEN "private"
END AS clientType,
Min(fb.from) as minFrom,
Max(fb.to) as maxTo
FROM
Booking b
INNER JOIN FacilityBooking fb ON b.id = fb.booking_id
GROUP BY bookId
) tbl
WHERE minFrom >= '2015-05-22' -- Min Date
AND maxTo <= '2015-05-24' -- Max Date
GROUP BY
clientType

MySQL Order by subquery column

I have problem with sql query. The idea is to select all loans that are after payment (status 1/2/3) between 8 and 21 days with calculated value from payment_day til now.
I have already done some query but can't use columns days_after_payment and days_after_part_payment in WHERE section. I would like to have one column like days_after_payment based on loan type.
SELECT l.*,
(SELECT SUM(`value`) FROM `loan_part` WHERE `loan_id` = l.id AND `paid`=0) AS left_to_pay,
-(DATEDIFF((SELECT date FROM `loan_part` WHERE `loan_id` = l.id AND `paid`=0 AND `date`<CURDATE() ORDER BY `date` LIMIT 1), NOW())) AS days_after_part_payment,
-(DATEDIFF(l.payment_date, NOW())) AS days_after_payment
FROM loan l
WHERE (l.type=1 or l.type=2) AND (l.status=1 OR l.status=2 OR l.status=3)
GROUP BY l.client_id
ORDER BY
CASE l.type
WHEN 1 THEN days_after_payment
WHEN 2 THEN days_after_part_payment
ELSE 1 END
ASC
CREATE TABLE IF NOT EXISTS `loan` (
`id` int(11) NOT NULL,
`value` int(11) NOT NULL,
`client_id` int(11) NOT NULL,
`status` int(11) NOT NULL,
`type` int(11) NOT NULL,
`payment_date` date DEFAULT NULL
) ENGINE=MyISAM AUTO_INCREMENT=2068 DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `loan_part` (
`id` int(10) unsigned NOT NULL,
`loan_id` int(11) NOT NULL,
`value` float NOT NULL,
`date` date DEFAULT NULL,
`paid` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=MyISAM AUTO_INCREMENT=1751 DEFAULT CHARSET=utf8;
Update1 : I had to cut unnecessary columns and rewrite it into English from my native language.
ORDER BY 7
"7" means the 7th field in the SELECT. That works for GROUP BY also. I had to see the table definition to count how many in l.*.
How come id is not declared AUTO_INCREMENT?

how to combine multiple select query into one?

i have three tables with structure
CREATE TABLE IF NOT EXISTS `refrence` (
`products_ref_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`categories_id` int(11) NOT NULL,
`Product_property` varchar(255) NOT NULL,
`Product_reference` varchar(50) NOT NULL,
PRIMARY KEY (`products_ref_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;
CREATE TABLE IF NOT EXISTS `product_details` (
`products_id` int(11) NOT NULL AUTO_INCREMENT,
`categories_id` int(11) NOT NULL,
`FieldA` varchar(30) NOT NULL,
`FieldB` varchar(50) NOT NULL,
`FieldC` varchar(255) NOT NULL,
`FieldD` varchar(255) NOT NULL,
`FieldE` varchar(255) NOT NULL,
`FieldF` varchar(255) NOT NULL,
`Field_desc` text NOT NULL,
PRIMARY KEY (`products_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=98 ;
Here i am extracting Product_reference value from refrence table with respect to Product_property & categories_id as
SELECT Product_reference FROM refrence where categories_id=3 AND Product_property ='xxx'
Now this Product_reference is the field name in product_details table suppose result is abc. So i want to extract distinct abc from product_details table as
SELECT Distinct abc FROM product_details where categories_id=3
Now i want to combine both the query to one, What is the way to write these queries in to single one.
I tried this
SELECT Distinct (
SELECT Product_reference FROM refrence where categories_id=3 AND Product_property ='xxx'
) as aa FROM product_details where categories_id=3
But its not working, may be i am missing some syntax.
Without knowing what isn't working, it would appear that you're trying to accomplish this:
SELECT r.product_reference
FROM refrence AS r
LEFT JOIN categories AS c ON (c.categories_id = r.categories_id AND r.categories_id=3 AND r.product_property='zzz')
WHERE c.categories_id=1
As pointed out by Eggyal, this will always return nothing because of the impossible condition of category_id=1 AND category_id=3, so either:
They both have to be 1 or both have to be 3
OR
They're completely different "categorys" that are unrelated, or just happen to have the same names.