Currently my schema looks like this:
CREATE TABLE IF NOT EXISTS `hours` (
`Project_ID` varchar(10) NOT NULL,
`Project_Name` varchar(50) NOT NULL,
`Res_ID` varchar(40) NOT NULL,
`Date` date NOT NULL,
`Hours` int(10) NOT NULL,
)
CREATE TABLE IF NOT EXISTS `project_resources` (
`Project_ID` varchar(10) NOT NULL,
`Res_ID` varchar(40) NOT NULL
)
//A single project Id can be assosiated with many resource id's
CREATE TABLE IF NOT EXISTS `resources` (
`Res_ID` varchar(40) NOT NULL,
`Res_Name` varchar(50) NOT NULL,
`Email` varchar(50) NOT NULL,
`Phone_Number` bigint(12) NOT NULL,
`Reporting_Manager` varchar(50) NOT NULL,
`Role` varchar(50) NOT NULL,
`Designation` varchar(50) NOT NULL,
`Password` varchar(50) NOT NULL
)
Here I am trying to generate a query such that it displays the data in the below format,
Resource Name | Sum(Hours).
I tried executing the following query
SELECT res_name,sum(hours) FROM hours h
INNER JOIN resources r ON h.res_id=r.res_id
WHERE r.res_id = (SELECT res_id FROM `project_resources` WHERE project_id='someproject')
I know this returns subquery returns more than 1 row error. But I was just wondering what I can do to get this query right.
I think this will help you
Select res_name,sum(hours)
from hours h inner join resources r on h.res_id=r.res_id
where r.res_id IN (
SELECT res_id
FROM `project_resources`
WHERE project_id='someproject'
)
you can use 'IN' clause in your where statement if your sub query return more than 1 rows
You can just use in:
Select res_name,sum(hours)
from hours h inner join
resources r
on h.res_id = r.res_id
where r.res_id in (SELECT res_id
FROM `project_resources`
WHERE project_id = 'someproject'
);
However, I might suggest just doing multiple joins:
Select res_name,sum(hours)
from hours h inner join
resources r
on h.res_id = r.res_id inner join
project_resources pr
on pr.res_id = r.res_id and pr.project_id = 'someproject'
Of course, this will not work if you have duplicates in the project_resources table.
So basically, you want to show 2 things:
Name of the resource
Total amount of hours the resource worked
Under the condition that your Res_ID is in project_resources table and the project id is 'someproject'.
Right?
Then let's break this problem into three small parts:
Part - 1:
To get the name of the resource you should write:
SELECT rs.Res_Name
FROM resources rs
Note that rs is the name of the alias of the table resources.
Ok?
Now Part - 2:
To get the total amount of hours the resource worked you should write:
SELECT SUM(Hours)
FROM hours h
Basically, h is the alias of the table hours. I think you got it, right?
Finally, Part - 3:
Your Project_ID should be 'someproject'.
Also, the Res_ID should be inside project_resources.
Now, let's join all the parts together. Now we get:
SELECT r.Res_Name, SUM(Hours)
FROM hours h
INNER JOIN resources r ON h.Res_ID = r.Res_ID
INNER JOIN project_resources pr ON r.Res_ID = pr.Res_ID
WHERE pr.Project_ID = 'someproject'
Basically, here we've first joined hours with resources given the fact that Res_ID is same in both tables, also we joined the table resources with project_resources given the fact that Res_ID is same in both tables, and that Project_ID of hours is 'someproject'.
Hopefully, this will give you what you want. You got the idea, right?
However, a word of caution. I've noticed that you're using the same name for table hours and it's column Hours. Although, this won't cause any problem because of the case difference in the names, but this is not really a good practice. You should think of a different, meaningful name for your column to avoid confusion and any kinds of unwanted occurences. Enjoy coding!!!
You are selecting total hours over all projects and their ressources that are (also) used in 'someproject'. As there can be more then one ressource associated with 'someproject', use IN to get them all. Then ...
either group by res_name (provided it is unique, else use res_id) to get a result record per ressource
or remove res_name from your result (because when there are more than one, you would only show one of them randomly)
or generate a string containing all ressource names with GROUP_CONCAT
So either:
SELECT
res_name,
sum(hours)
FROM hours h
INNER JOIN resources r ON h.res_id=r.res_id
WHERE r.res_id IN
(
SELECT res_id
FROM `project_resources`
WHERE project_id='someproject'
)
GROUP BY res_id;
Or:
SELECT
GROUP_CONCAT(res_name) AS res_names,
sum(hours)
FROM hours h
INNER JOIN resources r ON h.res_id=r.res_id
WHERE r.res_id IN
(
SELECT res_id
FROM `project_resources`
WHERE project_id='someproject'
);
Related
I would like to have some statistics and calculate the percentages of which tools have been chosen the most overall in all the registrations of my database
These are my two tables:
$table_registration = $wpdb->prefix . 'registration';
$table_tools = $wpdb->prefix . 'tools';
wp_registration table:
CREATE TABLE $table_registration
(
reg_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
dato date,
billedeURL VARCHAR(80) NOT NULL,
fiske_vaegt DECIMAL( 2,1 ) NOT NULL,
fiske_laengde INT NOT NULL,
reg_user_id BIGINT UNSIGNED NOT NULL,
reg_tools_id INT UNSIGNED NOT NULL
PRIMARY KEY (reg_id),
FOREIGN KEY (reg_user_id) REFERENCES wp_users(id),
FOREIGN KEY (reg_tools_id) REFERENCES $table_tools(tools_id)
)
wp_tools table:
CREATE TABLE $table_tools
(
tools_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
tools_navn CHAR (20),
PRIMARY KEY (tools_id)
)
I have been trying to create the correct mysql but with no luck so this is what I've been doing up till now.
select l.*, concat(round(100 * count(t.reg_tools_id) / t2.cnt,0),'%')
from wp_registration l
left join wp_tools t on l.toolss_id = t.reg_id
cross join
(select count(*) cnt
from wp_registration
where reg_tools_id = 1) t2
group by l.reg_id;
But it tells me that every tool has been used 50% of the times. which obviously is wrong I have three tools users can choose from and right now have 1 - two votes and 2 - nine votes and 3 - two votes there are 13 registrations in total
Hopefully, I understand what do you need !
SELECT
tools.tools_id,
((COUNT(*) / (SELECT COUNT(*) FROM registration)) * 100) AS percent
FROM
registration
JOIN
tools ON registration.reg_tools_id = tools.tools_id
GROUP BY
tools.tools_id
ORDER BY
percent DESC
LIMIT 1
Some remarks :
Try to write in a pure sql
You do not need a php tag for this question
Minimize your code from unnecessary part
Use the concat and round functions in the programming language that you are using not in SQL (I think you are using php here, so do the query then get the result and apply the round and the concat in php instructions)
I have two SQL Tables in my database
Structure of table1 is ie customer_classification is
CREATE TABLE IF NOT EXISTS `customer_classification` (
`sid` int(5) NOT NULL,
`customer_id` varchar(15) NOT NULL,
`classification` varchar(5) NOT NULL,
`appendix_id` int(5) NOT NULL,
`bill_date` date NOT NULL );
Structure of table2 is ie customer_consumption is
CREATE TABLE IF NOT EXISTS `customer_consumption`
`sid` int(5) NOT NULL,
`customer_id` varchar(25) NOT NULL,
`bill_date` date NOT NULL,
`reading` float NOT NULL,
`consumption` float NOT NULL,
`energy_bill` float NOT NULL,
`meter_rent` float NOT NULL,
`arrear` float NOT NULL );
In both tables, primary keys are customer_id and bill_date, because in a particular month there is only bill corresponding to single customer.
Now, my problem is, I am not able to merge these tables data into one to display the whole record.
I have tried this Sql Query, have a look
select co.customer_id, co.reading, co.consumption, cl.classification
from customer_consumption as co
INNER JOIN customer_classification as cl
on cl.customer_id = co.customer_id
and month(cl.bill_date) = month(co.bill_date)
where month(co.bill_date) = month(now())
It is not giving me the accurate result
I am going to guess that the consumption table has records for each month and the classification record only has records when the classification changes. If so, you want to get the most recent classification. And you can do that as:
select co.customer_id, co.reading, co.consumption,
(select cl.classification
from customer_classification as cl
where cl.customer_id = co.customer_id and
cl.bill_date <= co.bill_date
order by cl.bill_date desc
limit 1
) as classification
from customer_consumption co
where month(co.bill_date) = month(now());
Try this SQL query
select co.customer_id, sum(co.reading), sum(co.consumption), cl.classification
from customer_consumption as co
INNER JOIN customer_classification as cl
on cl.customer_id = co.customer_id
and month(cl.bill_date) = month(co.bill_date)
where month(co.bill_date) = month(now())
group by co.customer_id
i have an income and expense table. i want to select them both in a single query and get the difference between the income.amount field and expense.amount field grouping by month. so that the query will return a result whereby the total of expenses amount from the total of income amount basing the calculation on month. i used used two methods but non worked. find the below:
SELECT *, count(*), sum(`transaction`.amount) as tiamount, sum(expenditure.amount) as teamount, monthname(`transaction`.date) as mni, monthname(expenditure.date) as mne
FROM `transaction`, expenditure
WHERE month(expenditure.`date`)=month(`transaction`.`date`) and month(`transaction`.`date`)=month(expenditure.`date`)
GROUP BY monthname(`transaction`.date) ORDER BY `transaction`.date Desc
the other is :
SELECT count(*), `transaction`.date, sum(`transaction`.amount) as tiamount, sum(`transaction`.amount - expenditure.amount) as diff, monthname(`transaction`.date) as mni
FROM `transaction` left join expenditure on monthname(`transaction`.date) = monthname(expenditure.date)
UNION ALL
SELECT count(*), expenditure.date, sum(expenditure.amount) as teamount, sum(`transaction`.amount - expenditure.amount) as diff, monthname(expenditure.date) as mne
FROM expenditure left join `transaction` on monthname(`transaction`.date) = monthname(expenditure.date)
any help would be appreciated. Thanks.
ok thanks all. i solved the issue. each time the page is loaded, it checks to see if the table balance2 already exist, if it does it is dropped and recreated on the fly
mysql_query($query_truncate = "drop table IF EXISTS balance2");
$create= "CREATE TABLE balance2 (
`id` INT( 11 ) NOT NULL AUTO_INCREMENT ,
`count` VARCHAR( 50 ) NOT NULL DEFAULT '',
`month` VARCHAR( 50 ) NOT NULL DEFAULT '',
`amount` VARCHAR( 50 ) NOT NULL DEFAULT '',
`amount2` VARCHAR( 20 ) NOT NULL ,
`type` VARCHAR( 20 ) NOT NULL ,
`date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
PRIMARY KEY ( `id` )
) ENGINE = INNODB DEFAULT CHARSET = latin1";
mysql_query($create);
Then in used the INSERT INTO...SELECT...UNION SELECT....
to select from the expenses and income table to insert into the newly created balance table.
query_merge = "INSERT INTO balance2 (count, date, amount, amount2, month, type)
(SELECT count(*), t.date, sum(t.amount),0 ,monthname(t.date), 'income'
FROM `transaction` as t group by month(t.date))
UNION
(SELECT count(*), e.date, 0,sum(e.amount) as teamount, monthname(e.date) as mne, 'expense'
FROM expenditure as e group by month(e.date))";
this worked perfectly as it gave me all the results i wanted.
Thought it might help someone else. see ya
I currently have database setup like so:
CREATE TABLE `article` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` int(11) NOT NULL,
`body` int(11) NOT NULL,
`link` int(11) NOT NULL,
`date` datetime NOT NULL,
PRIMARY KEY (`id`)
)
CREATE TABLE `translation_pivot` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`content` text,
PRIMARY KEY (`id`)
)
This is quite a simplified version to illustrate the question, essentially the translation_pivot is used to further look up text strings from a series of language tables, but's that's not relevant. Here, the title, body and link columns in article contain an id referencing content from translation_pivot.
The difficulty is that doing an INNER JOIN will result in a column called content, which will contain only the first match from translation_pivot, in this case title.
The other option I've looked into is using GROUP_CONCAT on translation_pivot.content. This will work but then I'm left with a comma separated list of items and lose the obvious relation to title, body and link other than as the first, second and third items (which is ok, but not great). The more serious problem is that items in the translation can be several paragraphs of text. The default value for group_concat_max_len is 1024, which I can change but does this have performance implications if set to high values?
Ideally I'd like a way of replacing the title, body and link columns with the textual result from translation_pivot, or at least getting the textual content back for each as a separate column. Is this possible in a single query?
My other alternative is to retrieve key, value pairs as an array from translation_pivot with the id as the key and then do a lookup after querying the articles. This is only an extra query and probably a lot simpler.
Which solution will scale best? Or is there something else I'm missing?
Just do multiple joins:
SELECT
article.id AS id,
tptitle.content AS title,
tpbody.content AS body,
tplink.content AS link,
article.`date` AS `date`
FROM
article
INNER jOIN translation_pivot AS tptitle ON article.title=tptitle.id
INNER jOIN translation_pivot AS tpbody ON article.body=tpbody.id
INNER jOIN translation_pivot AS tplink ON article.link=tplink.id
or:
SELECT
article.id AS id,
IFNULL(tptitle.content,'DEFAULT TITLE') AS title,
IFNULL(tpbody.content, 'DEFAULT BODY') AS body,
IFNULL(tplink.content, 'DEFAULT LINK') AS link,
article.`date` AS `date`
FROM
article
LEFT jOIN translation_pivot AS tptitle ON article.title=tptitle.id
LEFT jOIN translation_pivot AS tpbody ON article.body=tpbody.id
LEFT jOIN translation_pivot AS tplink ON article.link=tplink.id
Link to the translation_pivot table for each of title, body and link - like so:
select a.`id`,
a.`date`,
t.`content` title_content,
b.`content` body_content,
l.`content` link_content
from `article` a
left join `translation_pivot` t on a.`title` = t.`id`
left join `translation_pivot` b on a.`body` = b.`id`
left join `translation_pivot` l on a.`link` = l.`id`
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.