So I have to tables, products_used aprox 600MB and products_language_description which is about 5MB, but the thing is that this query never finishes running...
I have tried REPAIR,OPTIMIZE ANALYZE, I'm out of ideas how to improve this...
SELECT pu.products_id, count(pu.products_id) as products_count, p.products_name,
pu.time_used FROM products_used pu, products_language_description p
WHERE pu.merchant_id='69'
AND p.products_id=pu.products_id GROUP BY products_id ORDER BY products_count
DESC LIMIT 0, 20
CREATE TABLE `products_used` (
`products_used_id` INT(15) NOT NULL AUTO_INCREMENT,
`plans_key` VARCHAR(255) NOT NULL DEFAULT '0',
`products_id` BIGINT(20) NOT NULL DEFAULT '0',
`customers_id` INT(10) NOT NULL DEFAULT '0',
`merchant_id` INT(10) NOT NULL DEFAULT '0',
`time_used` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`products_used_id`),
INDEX `plans_key` (`plans_key`)
)
COLLATE='latin1_swedish_ci'
ENGINE=MyISAM
AUTO_INCREMENT=24625441;
CREATE TABLE `products_language_description` (
`products_id` INT(5) NOT NULL DEFAULT '0',
`products_description` LONGTEXT NOT NULL,
`products_name` TEXT NOT NULL COLLATE 'utf8_general_ci',
`products_help_info` LONGBLOB NOT NULL,
`products_language` VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY (`products_id`, `products_language`)
)
COLLATE='latin1_swedish_ci'
ENGINE=MyISAM;
Try adding index to product_id & merchant_id fields on products_used table
Take a look at you WHERE section.
WHERE
pu.merchant_id='69'
AND p.products_id=pu.products_id GROUP BY products_id
You are comparing two different data types:
products_id INT(5) NOT NULL DEFAULT '0',
products_id BIGINT(20) NOT NULL DEFAULT '0',
Also, you are using single quotes in an unnecesary way:
pu.merchant_id='69'
Maybe you need to create some indeces in the first case. A foreign key helps too!
Maybe this helps you!
For this query:
SELECT pu.products_id, count(pu.products_id) as products_count,
p.products_name, pu.time_used
FROM products_used pu join
products_language_description p
on p.products_id = pu.products_id
WHERE pu.merchant_id = '69'
GROUP BY pu.products_id
ORDER BY products_count DESC
LIMIT 0, 20
You want an index on products_used(merchant_id, products_id). In MySQL, sometimes you can rewrite an aggregation query using a correlated subquery to improve results:
SELECT pu.products_id,
(select count(*)
from products_language_description p
where p.products_id = pu.products_id
) as products_count,
p.products_name, pu.time_used
FROM products_used pu
WHERE pu.merchant_id = '69'
ORDER BY products_count DESC
LIMIT 0, 20;
This replaces the outer group by with an aggregation in the correlated query that should just used the primary key index.
EDIT:
Wait. Your problem are these two definitions:
CREATE TABLE `products_used` (
. . .
`products_id` BIGINT(20) NOT NULL DEFAULT '0',
)
. . .
CREATE TABLE `products_language_description` (
`products_id` INT(5) NOT NULL DEFAULT '0',
. . .
)
The join conditions use a different data type. Fix the table structures so the columns have the same type (using alter table . . .) or by rebuilding them.
As Gordon mentioned, I would have an extend the index on your products used table to (merchant_id, product_id, time_used ) so it is a covering index and does not have to go to the raw data to get your count(). Now, it appear weird to me that you would have multiple instances of the SAME "products_id" in the products_used table for a given merchant, but that's another thing.
I would do an internal prequery of the product ID, count and time SPECIFIC TO THE MERCHANT you want. Otherwise, as in Gordon's query sample, I was prequerying EVERY Product first, then outside of that getting those for the merchant.
I am proposing prequery the internal products used specific to the merchant, then once THAT is returned, get the product name.
SELECT
JustByMerchant.products_id,
JustByMerchant.products_count,
p.products_name,
JustByMerchant.time_used
FROM
( select
pu.products_id,
count(*) as products_count,
pu.time_used
from
products_used pu
where
pu.merchant_id = 69
group by
pu.products_id
order by
COUNT(*) DESC
limit
0, 20 ) JustByMerchant
JOIN products_language_description p
ON JustByMerchant.products_id = p.products_id
Related
I'm trying to create a ranking table based on how many likes/upvotes a user had on all his items in total. User in the upvotes table links to id of the user that made the like, but I think you don't need this.
Hopefully by giving these tables everything will get clear.
I think the trick here is to get all the upvotes by each item and merge them together towards a user this item was from to get a total likes for each user and then rank all the users based on this total. Of course doing this will probably be a slow query so I need a very performant way to handle this.
The hard thing is here mainly that the upvotes table doesn't include the user id.
3 tables:
CREATE TABLE `items` (
`id` int(255) NOT NULL AUTO_INCREMENT,
`user_id` int(255) NOT NULL,
`img` varchar(500) NOT NULL,
`message` varchar(200) NOT NULL,
`created_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`active` int(11) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=19 DEFAULT CHARSET=latin1;
CREATE TABLE `upvotes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user` int(255) NOT NULL,
`item_id` int(255) NOT NULL,
`created_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
CREATE TABLE `users` (
`id` int(255) NOT NULL AUTO_INCREMENT,
`email` varchar(255) NOT NULL,
`password` binary(60) NOT NULL,
`first_name` varchar(255) NOT NULL,
`last_name` varchar(255) NOT NULL,
`active` int(1) NOT NULL DEFAULT '1',
`created_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`)
) ENGINE=MyISAM AUTO_INCREMENT=17 DEFAULT CHARSET=latin1;
I need a performant query giving me the ranking of each user ranked on how many likes they got on all their items?
I managed to write this:
SELECT #rank := #rank + 1 AS rank, m.*
FROM (SELECT
users.first_name as first_name,
users.last_name as last_name,
count(upvotes.item_id) as total
FROM upvotes
INNER JOIN users
ON users.id = (SELECT items.user_id FROM items WHERE items.id = upvotes.item_id LIMIT 1)
GROUP BY users.id
ORDER BY total DESC
) m, (SELECT #rank := 0) r
But I reckon this will be super slow when the database grows...
You can do a simple join query in order to get the total likes for each item of user and order your results with the resulting count in descending order
SELECT u.*,i.*,COUNT(DISTINCT up.user) `total_user_likes_item`
FROM users u
JOIN items i ON(i.user_id = u.id)
JOIN upvotes up ON(up.item_id = i.id)
GROUP BY u.id,i.id
ORDER BY u.id,i.id,total_user_likes_item DESC
Edit from comments For user total likes you remove i.id from group by as below query
SELECT u.*,COUNT(DISTINCT up.user) `total_user_likes_item`
FROM users u
JOIN items i ON(i.user_id = u.id)
JOIN upvotes up ON(up.item_id = i.id)
GROUP BY u.id
ORDER BY total_user_likes_item DESC
I'll try answer your question:
In table users you can add row sum_upvotes. Every time when someone get one like (vote) you will increment this column by:
UPDATE users
SET sum_upvotes = sum_upvotes + 1
;
Of course, you will insert a column in table upvotes.
Finally, you query to select users and order them by upvotes will look like this
SELECT first_name, last_name
FROM users
ORDER BY sum_upvotes
;
Hope this helps.
My query looks like
SELECT а.*, m.username, m.picture, m.picture_active
FROM questions_answer AS а
INNER JOIN members AS m ON а.poster_id=m.member_id
INNER JOIN questions AS q ON q.question_id=a.question_id
ORDER BY a.postdate DESC
I'm getting error:
Unknown column 'a.question_id' in 'on clause'
I don't know what is wrong, please help me with this.
Table questions is
CREATE TABLE IF NOT EXISTS `questions` (
`question_id` int(9) unsigned NOT NULL AUTO_INCREMENT,
`member_id` int(9) unsigned NOT NULL DEFAULT '0',
`question` text NOT NULL,
`postdate` int(10) unsigned NOT NULL DEFAULT '0',
`active` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`question_id`),
KEY `member_id` (`member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
and questions_answer is
CREATE TABLE IF NOT EXISTS `questions_answer` (
`answer_id` bigint(12) unsigned NOT NULL AUTO_INCREMENT,
`question_id` int(9) unsigned NOT NULL,
`poster_id` int(9) unsigned NOT NULL,
`body` text NOT NULL,
`postdate` int(9) unsigned NOT NULL,
PRIMARY KEY (`answer_id`),
KEY `question_id` (`question_id`),
KEY `poster_id` (`poster_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
Your table questions_answer may not have the column question_id
It would seem you don't have a column a.question_id
Check your structure for that table.
There is not any column called question_id in the questions_answer table.
Funny thing, if I use this code, it works:
SELECT qa.*
FROM questions_answer qa
INNER JOIN questions q ON qa.question_id = q.question_id
ORDER BY qa.postdate DESC
SQL-fiddle
So, to apply this to your query:
SELECT qa.*, m.username, m.picture, m.picture_active
FROM questions_answer qa
INNER JOIN questions q ON qa.question_id = q.question_id
INNER JOIN members m ON qa.poster_id = m.member_id
ORDER BY qa.postdate DESC
Your questions_answer table alias a has some issues. It most probably isn't the letter a.
FROM questions_answer AS а
I was playing around with it and found an error once that noted Unknown table 'аa': As you can see, it might be a special character. A short search on net came up with letter Eth
I replaced the alias with character a and the query works fine.
SELECT a.*, m.username, m.picture, m.picture_active
FROM questions_answer AS a
INNER JOIN members AS m ON a.poster_id=m.member_id
INNER JOIN questions AS q ON q.question_id=a.question_id
ORDER BY a.postdate DESC
See example here Just copy paste your query there and try to run, it will complain because of alias character issue. You can even copy the alias from you line and replace all as with it, it will work as well.
I have a seemingly simple task but I cannot seem to find an elegant solution using 1 query...
Problem:
I have a table of recorded 'clicks' on 'posts', where each post is part of a 'category'.
I want to find the 16 highest clicked posts in the last 30 days -- but I want to avoid duplicate categories.
It seems very simple actually, but I seem to be stuck.
I know how to get the most clicked in last 30, but I can't figure out how to avoid duplicate cats.
SELECT cat_id,
post_id,
COUNT(post_id) AS click_counter
FROM cs_coupon_clicks
WHERE time_of_click > DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY post_id
ORDER BY click_counter DESC
I tried to get creative/hacky with it... it's close but not correct:
SELECT cat_id,
Max(sort) AS sortid
FROM (SELECT cat_id,
post_id,
COUNT(post_id) AS click_counter,
CONCAT(COUNT(post_id), '-', post_id) AS sort
FROM cs_coupon_clicks
WHERE time_of_click > DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY cat_id, post_id) t1
GROUP BY cat_id
ORDER BY cat_id ASC
Any help would be greatly appreciated as I am not really a MySQL expert. I may end up just doing some PHP logic in the end, but I am very curious as to the correct way to approach a problem like this.
Thanks guys.
EDIT (structure):
CREATE TABLE `cs_coupon_clicks` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`src` varchar(255) NOT NULL DEFAULT '',
`cat_id` int(20) NOT NULL,
`post_id` int(20) NOT NULL,
`tag_id` int(20) NOT NULL,
`user_id` int(20) DEFAULT NULL,
`ip_address` char(30) DEFAULT NULL,
`referer` varchar(255) NOT NULL,
`browser` varchar(10) DEFAULT NULL,
`server_var` text NOT NULL,
`time_of_click` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `cat_id` (`cat_id`),
KEY `post_id` (`post_id`),
KEY `user_id` (`user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
TEMP WORKING SOLUTION (HACKY):
SELECT
cat_id,
MAX(sort) AS sortid
FROM (
SELECT
cat_id,
post_id,
COUNT(post_id) AS click_counter,
RIGHT(Concat('00000000', COUNT(post_id), '-', post_id), 16) AS SORT
FROM cs_coupon_clicks
WHERE time_of_click > DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY cat_id, post_id
) AS t1
GROUP BY cat_id
ORDER BY sortid DESC
There is no easy single query solution to this problem, it's a group-wise maximum kind of problem based on a temporary table (the one with counts) that would require self-joins.
Assuming your database grows big enough (otherwise just go for your php logic) I would go for a statistics table, holding info about categories, posts and click counts:
CREATE TABLE `click_cnts` (
`cat_id` int(20) NOT NULL,
`post_id` int(20) NOT NULL,
`clicks` int(20) NOT NULL,
PRIMARY KEY (`cat_id`,`post_id`),
KEY `cat_id` (`cat_id`,`clicks`)
)
and fill it using the same query as the first one in the question:
INSERT INTO click_cnts(cat_id, post_id, clicks)
SELECT cat_id, post_id, COUNT(post_id) AS click_counter
FROM cs_coupon_clicks
WHERE time_of_click > NOW() - INTERVAL 30 DAY
GROUP BY cat_id,post_id
You could update this table using triggers or running update query periodically (do users really need info up to the very last second? probably not...) and save a lot of processing as finding most clicks for each category on indexed table requires a lot less time using a classic group-wise max approach:
SELECT cg.cat_id, cu.post_id, cg.most_clicks
FROM
( SELECT cat_id, max(clicks) as most_clicks FROM click_cnts
GROUP BY cat_id ) cg
JOIN click_cnts cu
ON cg.cat_id = cu.cat_id
AND cu.post_id = ( SELECT cc.post_id FROM click_cnts cc
WHERE cc.cat_id = cg.cat_id
AND cc.clicks = cg.most_clicks
LIMIT 1 )
ORDER BY cg.most_clicks DESC
LIMIT 16
Shot in the dark here. Did you try Select DISTINCT cat_id
I am trying to make a printable page, where there is all the sales of a specified manufacturer, listing all the products, between specified dates. If there has not been any sales, it should display 0.
The tables
// Manufacturer table
// mid, manufacturer
// Products table
// pid, product, ref_manufacturer_id
// Orders table
// oid, orderPrice, orderDateTime, ref_product_id
And the query that works (without date limitation)
SELECT prod.product, COALESCE(COUNT(pord.oid),0) AS orderCount,
COALESCE(SUM(pord.orderPrice),0) AS orderSum
FROM product_manufacturer AS manu
JOIN product_list AS prod ON prod.ref_manufacturer_id = manu.mid
LEFT JOIN product_orders AS pord ON pord.ref_product_id = prod.pid
WHERE manu.mid = :manu_id
GROUP BY prod.product;
But as soon as I add into the WHERE-syntax this
WHERE manu.mid = :manu_id AND DATE(pord.orderDateTime) BETWEEN :orders_start AND :orders_end
I am using PHP PDO on connecting and verifying that the manu_id is int and the orders_start/end is converted to MySQL date format.
But the question I am trying to fidn out is, what is causing the problem, that when I add the date restriction, every product that was not ordered, is not displayed on the output?
SQL on creating the tables
CREATE TABLE product_list (
pid bigint(20) unsigned NOT NULL AUTO_INCREMENT,
product varchar(255) NOT NULL,
ref_manufacturer_id bigint(20) unsigned NOT NULL,
PRIMARY KEY (pid),
KEY ref_manufacturer_id (ref_manufacturer_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE product_manufacturer (
mid bigint(20) unsigned NOT NULL AUTO_INCREMENT,
manufacturer varchar(255) NOT NULL,
PRIMARY KEY (mid),
UNIQUE KEY manufacturer (manufacturer)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE product_orders (
oid bigint(20) unsigned NOT NULL AUTO_INCREMENT,
orderPrice float(10,2) NOT NULL,
orderDatetime timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
red_product_id bigint(20) unsigned NOT NULL,
PRIMARY KEY (oid),
KEY red_product_id (red_product_id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
What you need is to move the orderDateTime criteria to the join clause instead of where clause like:
SELECT prod.product, COALESCE(COUNT(pord.oid),0) AS orderCount,
COALESCE(SUM(pord.orderPrice),0) AS orderSum
FROM product_manufacturer AS manu
JOIN product_list AS prod ON prod.ref_manufacturer_id = manu.mid
LEFT JOIN product_orders AS pord
ON pord.ref_product_id = prod.pid
AND DATE(pord.orderDateTime) BETWEEN :orders_start AND :orders_end
WHERE manu.mid = :manu_id
GROUP BY prod.product;
The reason it does not work within the WHERE clause is because of the NULL values returned from the outer join. When you do not have a row in product_orders fot a product, the outer join returns a NULL for the date field orderDateTime and that row will be filtered out because a NULL is not equal to anything.
Try:
SELECT p.product,
COALESCE(o.orderCount, 0) as orderCount,
COALESCE(o.orderSum,0) AS orderSum
FROM product_manufacturer AS m
JOIN product_list AS p ON p.ref_manufacturer_id = m.mid
LEFT JOIN (
SELECT ref_product_id as pid, COUNT(oid) AS orderCount, SUM(orderPrice) AS orderSum
FROM product_orders
WHERE DATE(orderDateTime) BETWEEN :orders_start AND :orders_end
GROUP BY ref_product_id
) AS o ON p.pid = o.pid
WHERE m.mid = :manu_id
Edit: Corrected after ypercube comment.
try this on the where clause.
WHERE manu.mid = :manu_id AND (DATE(pord.orderDateTime) BETWEEN :orders_start AND :orders_end)
It might be reading the second AND function as another where clause that the statement should return true. Just a hunch on that. Let me know if this does the trick.
I don't know how your specific system works, but it may be orderDateTime is not set (ie, NULL or something else) until that product gets ordered. You may want to try:
WHERE manu.mid = :manu_id AND ((DATE(pord.orderDateTime) BETWEEN :orders_start AND :orders_end) OR pord.orderDateTime=NULL)
If this is not the case, could you give an example of the orderDateTime value for something that is not showing up when you want it to?
I am currently developing a an application to allow users to search through a database of documents using various paramaters and returning a set of paged results. I am building it in PHP/MySQL, which is not my usual development platform, but its been grand so far.
The problem I am having is that in order to return a full set of results I have to use LEFT JOIN on every table, which completely destroys my performance. The person who developed the database has said that the query I am using will return the correct results, so thats what I have to use. The query is below, I am by no means an SQL Guru and could use some help on this.
I have been thinking that it might be better to split the query into sub-queries? Below is my current query:
SELECT d.title, d.deposition_id, d.folio_start, d.folio_end, pl.place_id, p.surname, p.forename, p.person_type_id, pt.person_type_desc, p.age, d.manuscript_number, dt.day, dt.month, dt.year, plc.county_id, c.county_desc
FROM deposition d
LEFT JOIN person AS p ON p.deposition_id = d.deposition_id
LEFT JOIN person_type AS pt ON p.person_type_id = pt.person_type_id
LEFT JOIN place_link AS pl ON pl.deposition_id = d.deposition_id
LEFT JOIN date AS dt ON dt.deposition_id = d.deposition_id
LEFT JOIN place AS plc ON pl.place_id = plc.place_id
LEFT JOIN county AS c ON plc.county_id = c.county_id
WHERE 1 AND d.manuscript_number = '840'
GROUP BY d.deposition_id ORDER BY d.folio_start ASC
LIMIT 0, 20
Any help or guidance would be greatly appreciated!
Deposition Table:
CREATE TABLE IF NOT EXISTS `deposition` (
`deposition_id` varchar(11) NOT NULL default '',
`manuscript_number` int(10) NOT NULL default '0',
`folio_start` varchar(4) NOT NULL default '0',
`folio_end` varchar(4) default '0',
`page` int(4) default NULL,
`deposition_type_id` int(10) NOT NULL default '0',
`comments` varchar(255) default '',
`title` varchar(255) default NULL,
PRIMARY KEY (`deposition_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Date Table
CREATE TABLE IF NOT EXISTS `date` (
`deposition_id` varchar(11) NOT NULL default '',
`day` int(2) default NULL,
`month` int(2) default NULL,
`year` int(4) default NULL,
PRIMARY KEY (`deposition_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Person_Type
CREATE TABLE IF NOT EXISTS `person_type` (
`person_type_id` int(10) NOT NULL auto_increment,
`person_type_desc` varchar(255) NOT NULL default '',
PRIMARY KEY (`person_type_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=59 ;
Seems that you want to select one person, place etc. per deposition.
The query you wrote will return you this, but it's not guaranteed which one will it return, and the query is inefficient.
Try this:
SELECT d.title, d.deposition_id, d.folio_start, d.folio_end, pl.place_id, p.surname, p.forename, p.person_type_id, pt.person_type_desc, p.age, d.manuscript_number, dt.day, dt.month, dt.year, plc.county_id, c.county_desc
FROM deposition d
LEFT JOIN
person p
ON p.id =
(
SELECT id
FROM person pi
WHERE pi.deposition_id = d.deposition_id
ORDER BY
pi.deposition_id, pi.id
LIMIT 1
)
LEFT JOIN
place_link AS pl
ON pl.id =
(
SELECT id
FROM place_link AS pli
WHERE pli.deposition_id = d.deposition_id
ORDER BY
pli.deposition_id, pi.id
LIMIT 1
)
LEFT JOIN
date AS dt
ON dt.id =
(
SELECT id
FROM date AS dti
WHERE dti.deposition_id = d.deposition_id
ORDER BY
dti.deposition_id, pi.id
LIMIT 1
)
LEFT JOIN
place AS plc
ON plc.place_id = pl.place_id
LEFT JOIN
county AS c
ON c.county_id = plc.county_id
WHERE d.manuscript_number = '840'
ORDER BY
d.manuscript_number, d.folio_start
LIMIT 20
Create an index on deposition (manuscript_number, folio_start) for this to work fast
Also create a composite index on (deposition_id, id) on person, place_link and date.
The poor performance is almost certainly from lack of indexes. Your deposition table doesn't have any indexes, and that probably means the other tables you're referencing don't have any either. You can start by adding an index to your deposition table. From the MySQL shell, or phpMyAdmin, issue the following query.
ALTER TABLE deposition ADD INDEX(deposition_id, manuscript_number);
You know you're on the right track if the query executes faster after adding the index. From there you might want to put indexes on the other tables on the referenced columns. For instance for this part of your query "LEFT JOIN person AS p ON p.deposition_id = d.deposition_id", you could try adding an index to the person table using.
ALTER TABLE person ADD INDEX(deposition_id);
You only need a LEFT JOIN if the joined table might not have a matching value. Is it possible in your database schema for a person to not have a matching person_type? Or deposition to not have a matching row in date? A place not have a matching county?
For any of those relationships that must exist for the result to make sense you can change the LEFT JOIN to an INNER JOIN.
These columns should have indexes (unique if possible):
person.deposition_id
date.deposition_id
place_link.deposition_id
place_link.place_id
The date table looks like a bad design; I can't think of a reason to have a table of dates instead of just putting a column of type date (or datetime) in the deposition table. And date is a terrible name for a table because it's a SQL reserved word.