SQL: Subquery assistance - sql

I'm trying to SELECT two values in one statement instead of two statements. The first statement counts how many entries won for a specific contest that has an id of 18. The second statement counts the total quantity of prizes for that contest.
Query 1:
SELECT
COUNT(*) FROM entries
WHERE entries.contest=18
AND entries.won=1
Query 2
SELECT SUM( quantity ) FROM prizes WHERE contest=18
I want to have both so I could compare them on the server-side and act upon that comparison.. So if we had say 3 entrants who won the 18th contest and the total quantity of prizes for the 18th contest is 5, do something...
Schema:
CREATE TABLE `entries` (
`id` int(2) unsigned NOT NULL auto_increment,
`message_id` bigint(8) unsigned default NULL,
`user` int(2) unsigned default NULL,
`username` varchar(50) NOT NULL,
`contest` int(2) unsigned default NULL,
`message` text,
`icon` text,
`twitter_url` text,
`follower` int(2) unsigned default NULL,
`entry_date` timestamp NOT NULL default '0000-00-00 00:00:00',
`won` int(1) default '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
CREATE TABLE `prizes` (
`id` int(2) unsigned NOT NULL auto_increment,
`name` varchar(25) NOT NULL,
`contest` int(2) NOT NULL,
`quantity` int(2) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
There is no users table because I control the entries, and I'm pretty sure I won't get any duplicates so that's why the user name, etc is stored for the entry row.

As the queries doesn't have anything in common at all, you would use two subqueries to get them in the same result:
select
(select count(*) from entries where contest = 18 and won = 1) as wins,
(select sum(quantity) from prizes where contest = 18) as quantity

Related

SQL find all records from one table, but only the most recent record from a linked table

I am looking to select all records from my userrecords table and then find the corresponding most recent record from my checkins table.
I need to do this so that I can display whether the user is checked-in or checked-out for the current day and campus.
The outdatetime is a NULL value when the user is currently checked-in, and the query should take into account the current date so that only checkin records for the current date are considered.
My table setup is like so:
CREATE TABLE `userrecords` (
`userid` int(11) NOT NULL AUTO_INCREMENT,
`firstname` varchar(45) NOT NULL,
`surname` varchar(45) NOT NULL,
`email` varchar(45) NOT NULL,
`phone` varchar(15) DEFAULT NULL,
`password` char(64) DEFAULT NULL,
`userlevel` int(1) NOT NULL,
`suspended` varchar(1) DEFAULT NULL,
`created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`lastcheckdate` datetime DEFAULT NULL,
`maincampus` varchar(3) DEFAULT NULL,
`lastlogin` datetime DEFAULT NULL,
`staffid` varchar(6) DEFAULT NULL,
PRIMARY KEY (`userid`),
UNIQUE KEY `email_UNIQUE` (`email`),
UNIQUE KEY `userid_UNIQUE` (`userid`)
) ENGINE=InnoDB AU
CREATE TABLE `checkins` (
`recordid` int(11) NOT NULL AUTO_INCREMENT,
`userid` int(11) NOT NULL,
`campusid` int(11) NOT NULL,
`indatetime` datetime DEFAULT NULL,
`outdatetime` datetime DEFAULT NULL,
PRIMARY KEY (`recordid`),
KEY `campusid_idx` (`campusid`),
KEY `userid_idx` (`userid`),
CONSTRAINT `campusid` FOREIGN KEY (`campusid`) REFERENCES `campus` (`campusid`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `userid` FOREIGN KEY (`userid`) REFERENCES `userrecords` (`userid`) ON DELETE CASCADE ON UPDATE CASCADE
My query is so far:
SELECT userid,firstname,surname,email,lastcheckdate FROM userrecords WHERE userlevel=0
You can use window functions:
select ur.*, c.*
from userrecords ur left join
(select c.*,
row_number() over (partition by userid order by indatetime desc) as seqnum
from checkins c
) c
on ur.userid = c.userid and ur.seqnum = 1;

SQL-Statement for counting and calculating relational data

I have a main-table "Restaurants" and an one-to-many relation to a "RestaurantVotes" table. One Restaurant can have multiple Votes. It's defined like this:
CREATE TABLE `restaurant_votes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`created_at` datetime NOT NULL,
`user_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`cat_food` int(11) NOT NULL,
`cat_cart` int(11) NOT NULL,
`cat_ambassador` int(11) NOT NULL,
`cat_drinks` int(11) NOT NULL,
`cat_service` int(11) NOT NULL,
`cat_ambience` int(11) NOT NULL,
`cat_price` int(11) NOT NULL,
`restaurant_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `IDX_1B96C91EB1E7706E` (`restaurant_id`),
CONSTRAINT `FK_1B96C91EB1E7706E` FOREIGN KEY (`restaurant_id`) REFERENCES `restaurants` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `restaurants` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
For an API-call we need to get all restaurants including name and the overall rating for this restaurant as a JSON response. Name etc. is no the problem, but we have big problems to find a SQL-query which gets the rating for each restaurant. The rating is calculated like this:
Summerize the values (integer 1-5) of all categories (cat_*) and divide it by the number of the categories (7).
Summerize the result of the ratings above and divide the value by the number of ratings in the restaurant_votes table per restaurant.
Return the overall rating from 2. in the SQL-query together with the name of the restaurant.
Is this even possible using SQL? Any advice? Thanks!
select
r.name,
sum(rv.cat_food +
rv.cat_cart +
rv.cat_ambassador +
rv.cat_drinks +
rv.cat_service +
rv.cat_ambience +
rv.cat_price) / 7 as total_score,
sum(rv.cat_food +
rv.cat_cart +
rv.cat_ambassador +
rv.cat_drinks +
rv.cat_service +
rv.cat_ambience +
rv.cat_price) / 7 / count(rv.id) as average_score
from restaurants r
left join restaurant_votes rv
on r.id = rv.restaurant_id
group by r.name
I've posted a SQLFiddle to demonstrate this.
SELECT r.name,r.id,score.rating
FROM
restaurants as r
INNER JOIN
(
SELECT restaurant_id,AVG(cat_food + cat_cart + cat_ambassador + cat_drinks + cat_service + cat_ambience + cat_price)/7 AS rating
FROM restaurant_votes
GROUP BY restaurant_id
)as score
ON r.id = score.rating
Would this work?
It gets all the restaurants defined in the restaurant table and and gets the average of all the columns added together and divided by 7.

Insert data into 5 tables from a html form

I have a problem how to place an order for a product, and then insert the order into 5 tables that are connected: ONE-TO-MANY, The tables are so connected that when the customer comes in the room, place an order for a product for e.g. coffee, or water it has to show in the orders page who placed the order, in which room does the customer sits and then the waiter gets the order into from the status is the product paid or not.
the tables are:
CREATE TABLE IF NOT EXISTS `user` (
`user_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`type_id` int(10) unsigned NOT NULL,
`username` varchar(50) DEFAULT NULL,
`password` varchar(32) DEFAULT NULL,
`first_name` varchar(100) DEFAULT NULL,
`last_name` varchar(100) DEFAULT NULL,
`email` varchar(50) DEFAULT NULL,
`picture` varchar(200) DEFAULT NULL,
PRIMARY KEY (`user_id`),
UNIQUE KEY `user_index1780` (`username`),
KEY `user_FKIndex1` (`type_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=71 ;
CREATE TABLE IF NOT EXISTS `order` (
`order_id` int(11) NOT NULL AUTO_INCREMENT,
`time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`status` int(11) DEFAULT NULL,
`room_id` int(11) NOT NULL,
`user_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`order_id`),
KEY `fk_order_room1` (`room_id`),
KEY `fk_order_user2` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=10 ;
CREATE TABLE IF NOT EXISTS `product` (
`product_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL,
`price` float DEFAULT NULL,
`picture` varchar(500) DEFAULT NULL,
PRIMARY KEY (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=12 ;
CREATE TABLE IF NOT EXISTS `room` (
`room_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL,
`picture` varchar(450) DEFAULT NULL,
`description` text,
`user_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`room_id`),
KEY `fk_room_user1` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
CREATE TABLE IF NOT EXISTS `item_orders` (
`order_id` int(11) NOT NULL,
`product_id` int(11) NOT NULL,
`quantity` int(11) DEFAULT NULL,
PRIMARY KEY (`order_id`,`product_id`),
KEY `fk_order_has_product_product1` (`product_id`),
KEY `fk_order_has_product_order1` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `type_user` (
`type_id` int(10) unsigned NOT NULL,
`name` varchar(20) DEFAULT NULL,
PRIMARY KEY (`type_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Now after I inner join them to place an order for a product nothing's happening.
The query is :
SELECT order.order_id,
user.first_name AS user,
product.name,
product.price,
item_orders.quantity,
product.price * item_orders.quantity AS sum,
room.name,
order.time,
order.status
FROM user, product, room, `order`, item_orders
WHERE user.user_id = room.user_id
AND order.room_id = room.room_id
AND order.order_id = item_orders.order_id
AND product.product_id = item_orders.product_id
This join is just fine it has only to enter a new join order_id = '$order_id' and that is it.
Provided you inserted data in all the tables referenced from your query:
INSERT INTO type_user (type_id) VALUES (1);
INSERT INTO user (user_id, type_id, first_name) VALUES (1, 1, 'cappie');
INSERT INTO room (room_id, user_id, name) VALUES (10, 1, 'Room 10');
INSERT INTO `order` (order_id, room_id, user_id, status) VALUES (1 , 10, 1, 3);
INSERT INTO product (product_id, name, price) VALUES (100, 'Product A', 99.99);
INSERT INTO item_orders (order_id, product_id, quantity) VALUES (1, 100, 15);
The query you provided (in the comments) will run fine and return (almost) correct Results :
| ORDER_ID | USER | NAME | PRICE | QUANTITY | SUM | TIME | STATUS |
--------------------------------------------------------------------------------------------------------------------------
| 1 | cappie | Product A | 99.98999786377 | 15 | 1499.849967956543 | August, 08 2013 07:03:34+0000 | 3 |
You have a floating point rounding error, but that's a story for another day.
Here is the full SQL Fiddle.

How to use subqueries in MySQL

I have two tables.
Table user:
CREATE TABLE IF NOT EXISTS `user` (
`user_id` int(20) NOT NULL AUTO_INCREMENT,
`ud_id` varchar(50) NOT NULL,
`name` text NOT NULL,
`password` text NOT NULL,
`email` varchar(200) NOT NULL,
`image` varchar(150) NOT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB
and mycatch:
CREATE TABLE IF NOT EXISTS `mycatch` (
`catch_id` int(11) NOT NULL AUTO_INCREMENT,
`catch_name` text NOT NULL,
`catch_details` text NOT NULL,
`longitude` float(10,6) NOT NULL,
`latitude` float(10,6) NOT NULL,
`time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`image` varchar(150) NOT NULL,
`user_id` int(20) NOT NULL,
PRIMARY KEY (`catch_id`),
KEY `user_id` (`user_id`)
) ENGINE=InnoDB;
ALTER TABLE `mycatch`
ADD CONSTRAINT `mycatch_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`) ON DELETE CASCADE ON UPDATE CASCADE;
My goal is: I want to retrieve longitude and latitude from mycatch against given ud_id (from user) and catch_id (from mycatch) where ud_id = given ud_id and catch_id > given catch_id.
I used the query but fail to retrieve
SELECT ud_id=$ud_id FROM user
WHERE user_id =
(
SELECT user_id,longitude,latitude from mycatch
WHERE catch_id>'$catch_id'
)
The error is:
#1241 - Operand should contain 1 column(s)
First, try not to use subqueries at all, they're very slow in MySQL.
Second, a subquery wouldn't even help here. This is a regular join (no, Mr Singh, not an inner join):
SELECT ud_id FROM user, mycatch
WHERE catch_id>'$catch_id'
AND user.user_id = mycatch.user_id
Select m.longitude,m.latitude from user u left join mycatch m
on u.user_id =m.user_id
where u.ud_id=$ud_id and
m.catch_id >$catch_id

Cant figure out how to join these tables

Need help with some kind of join here. Cant figure it out.
I want to loop out the forum boards, but I also want to get last_post and last_posterid from posts, and based on last_posterid get username from users.
This is how far I've come yet (:P):
SELECT name, desc, position FROM boards b
INNER JOIN posts p ON ???
INNER JOIN users u ON ???
ORDER BY b.position ASC
Help would be greatly appreciated.
Cheers
CREATE TABLE `posts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`posterid` int(10) unsigned NOT NULL DEFAULT '0',
`subject` varchar(255) DEFAULT NULL,
`message` text,
`posted` int(10) unsigned NOT NULL DEFAULT '0',
`edited` int(10) unsigned DEFAULT NULL,
`edited_by` int(10) unsigned NOT NULL DEFAULT '0',
`icon` varchar(255) DEFAULT NULL,
`topicid` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
CREATE TABLE `boards` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL,
`desc` varchar(255) NOT NULL,
`position` int(10) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=9 DEFAULT CHARSET=utf8
CREATE TABLE `users` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(200) NOT NULL,
`password` varchar(255) NOT NULL,
`salt` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=9 DEFAULT CHARSET=utf8
CREATE TABLE `topics` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`posterid` int(10) unsigned NOT NULL DEFAULT '0',
`subject` varchar(255) NOT NULL,
`posted` int(10) unsigned NOT NULL DEFAULT '0',
`last_post` int(10) unsigned NOT NULL DEFAULT '0',
`last_poster` int(10) unsigned NOT NULL DEFAULT '0',
`num_views` mediumint(8) unsigned NOT NULL DEFAULT '0',
`num_replies` mediumint(8) unsigned NOT NULL DEFAULT '0',
`closed` tinyint(1) NOT NULL DEFAULT '0',
`sticky` tinyint(1) NOT NULL DEFAULT '0',
`icon` varchar(40) DEFAULT NULL,
`boardid` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=16 DEFAULT CHARSET=utf8
Assuming posts.topicid refers to boards.id, and posts.posterid refers to users.id, try something like
SELECT b.name, d.desc, b.position
FROM boards b
LEFT JOIN posts p
ON b.id = p.topicid
LEFT JOIN users u
ON (p.posterid = u.id) AND (p.posted = (SELECT MAX(sp.posted) FROM posts sp GROUP BY))
ORDER BY
b.position ASC
Another remark: try to name your fields such that it is clear what they refer to, for instance:
CREATE TABLE `foo` (
foo_ID UNSIGNED INTEGER NOT NULL AUTO_INCREMENT,
-- more stuff
PRIMARY KEY (`foo_ID`)
);
This allows you to re-use the field name foo_ID in another table, which makes things very easy when performing a join.
Nothing stands out, so this is mostly a guess:
SELECT b.name, b.desc, b.position
FROM boards AS b
INNER JOIN posts AS p
ON b.id = p.topicid
INNER JOIN users AS u
ON p.posterid = u.id
ORDER BY b.position ASC
SELECT username, message, posts.id
FROM users, posts
WHERE posterid=users.id
GROUP BY username, message, posts.id
HAVING posted=MAX(posted)
This might just do to get each user's last post concisely.
Ok, lack of a schema not withstanding, it looks like you could use nested select. Something like:
DECLARE #posts TABLE
(
postdate datetime,
postid int,
boardid int
)
DECLARE #users TABLE
(
userid int,
username varchar(10)
)
DECLARE #boards TABLE
(
boardid int
)
INSERT INTO #boards(boardid) VALUES (1)
INSERT INTO #users (userid, username) values (1, 'Adam')
insert into #posts (postdate, postid, boardid) values (GETDATE(), 1, 1)
SELECT b.*, p.*
from #boards b
inner join
(
SELECT TOP 1 postdate, postid, boardid, username FROM #posts
inner join #users on userid = postid WHERE boardid = 1
order by postdate desc
) as p on p.boardid = b.boardid
Oviously this is heavily generalised and uses some hard-coded values, but hopefully it should give you an option for implementing it on your own tables.
Of course, I infer no correctness in my implementation of the spec - SQL is not my first language lol
Was going to post this as a comment but it was too verbose and messy.
you'll want to use OUTER JOINs because otherwise boards with no posts in them will not show up
any single query that links those four tables is going to be a beast - consider using VIEWs to simplify things
your initial question and the tables you posted leave things a bit ambiguous: do you already have last_post and last_poster filled out in topics? If so, then your primary targets for the join should be boards and topics, and since you're already storing redundant data (I assume for speed?), why don't you also put in a last_post_time or something, which would simplify things a lot and curb load (no need to join in posts at all)?
mediumints? why on Earth? (If you don't need the date, and you think it'll never wrap, I suppose you can use the hack of trusting that a later post will have a higher ID due to autoincrementing key...)