I am writing a query against an advanced many-to-many table in my database. I call it an advanced table because it is a many-to-many table with and extra field. The table maps data between the fields table and the students table. The fields table holds potential fields that a student can used, kind of like a contact system (i.e. name, school, address, etc). The studentvalues table that I need to query against holds the field id, student id, and the field answer (i.e. studentid=1; fieldid=2; response=Dave Long).
So my table looks like this:
What I need to do is take a few passed in values and create a grouped accumulated report. I would like to do as much in the SQL as possible.
So that data that I have will be the group by field (a field id), the cumulative field (a field id) and I need to group the students by the group by field and then in each group count the amount of students in the cumulative fields.
So for example I have this data
ID STUDENTID FIELDID RESPONSE
1 1 2 *(city)* Wallingford
2 1 3 *(state)* CT
3 2 2 *(city)* Wallingford
4 2 3 *(state)* CT
5 3 2 *(city)* Berlin
6 3 3 *(state)* CT
7 4 2 *(city)* Costa Mesa
8 4 3 *(state)* CA
I am hoping to write one query that I can generate a report that looks like this:
CA - 1 Student
Costa Mesa 1
CT - 3 Students
Berlin 1
Wallingford 2
Is this possible to do with a single SQL statement or do I have to get all the groups and then loop over them?
EDIT Here is the code that I have gotten so far, but it doesn't give the proper stateSubtotal (the stateSubtotal is the same as the citySubtotal)
SELECT state, count(state) AS stateSubtotal, city, count(city) AS citySubtotal
FROM(
SELECT s1.response AS city, s2.response AS state
FROM studentvalues s1
INNER JOIN studentvalues s2
ON s1.studentid = s2.studentid
WHERE s1.fieldid = 5
AND s2.fieldid = 6
) t
GROUP BY city, state
So to make a table that looks like that, I would assume something like
State StateSubtotal City CitySubtotal
CA 1 Costa Mesa 1
CT 3 Berlin 1
CT 3 Wallingford 2
Would be what you want. We can't just group on Response, since if you had a student answer LA for city, and another student that responds LA for state (Louisiana) they would add. Also, if the same city is in different states, we need to first lay out the association between a city and a state by joining on the student id.
edit - indeed, flawed first approach. The different aggregates need different groupings, so really, one select per aggregation is required. This gives the right result but it's ugly and I bet it could be improved on. If you were on SQL Server I would think a CTE would help but that's not an option.
select t2.stateAbb, stateSubtotal, t2.city, t2.citySubtotal from
(
select city, count(city) as citySubTotal, stateAbb from (
select s1.Response as city, s2.Response as StateAbb
from aaa s1 inner join aaa s2 on s1.studentId = s2.studentId
where s1.fieldId = 2 and s2.fieldId=3
) t1
group by city, stateabb
) t2 inner join (
select stateAbb, count(stateabb) as stateSubTotal from (
select s1.Response as city, s2.Response as StateAbb
from aaa s1 inner join aaa s2 on s1.studentId = s2.studentId
where s1.fieldId = 2 and s2.fieldId=3
) t3
group by stateabb
) t4 on t2.stateabb = t4.stateabb
Related
I essentially like to have one query which I'll execute one time and like to have the result (no multiple query execution) and definitely, the query should use simple MySQL structure (no complex/advanced structure to be used like BEGIN, loop, cursor).
Say I've two tables.
1st Table = Country (id(PK), name);
2nd Table = Businessman (id(PK), name, city, country_id(FK))
Like to SELECT all countries, whose businessmen are from distinct cities. No two businessmen exist in one country, who are from the same city. If so, that country will not be selected by the SELECT clause.
Country
id name
1 India
2 China
3 Bahrain
4 Finland
5 Germany
6 France
Businessman
id name city country_id
1 BM1 Kolkata 1
2 BM2 Delhi 1
3 BM3 Mumbai 1
4 BM4 Beijing 2
5 BM5 Paris 6
6 BM6 Beijing 2
7 BM7 Forssa 4
8 BM8 Anqing 2
9 BM9 Berlin 5
10 BM10 Riffa 3
11 BM11 Nice 6
12 BM12 Helsinki 4
13 BM13 Bremen 5
14 BM14 Wiesbaden 5
15 BM15 Angers 6
16 BM16 Sitra 3
17 BM17 Adliya 3
18 BM18 Caen 6
19 BM19 Jinjiang 2
20 BM20 Tubli 3
21 BM21 Duisburg 5
22 BM22 Helsinki 4
23 BM23 Kaarina 4
24 BM24 Bonn 5
25 BM25 Kemi 4
In this respect, China and Finland shouldn't be listed.
I've attempted using count and group by, but no luck.
Can you please help me to build up this query.
Here it is, all you need is to join Businessman table and count cities and distinct cities and if they equal that means all businessmen are from different cities:
SELECT
c.`id`,
c.`name`,
COUNT(b.`id`) AS BusinessmanCount,
COUNT(b.`city`) AS CityCount,
COUNT(DISTINCT b.`city`) AS DistinctCityCount
FROM `countries` c
INNER JOIN Businessman b ON c.`id` = b.`country_id`
GROUP BY c.`id`
HAVING CityCount = DistinctCityCount
For minified version what you exactly need:
SELECT
c.`id`,
c.`name`
FROM `countries` c
INNER JOIN Businessman b ON c.`id` = b.`country_id`
GROUP BY c.`id`
HAVING COUNT(b.`city`) = COUNT(DISTINCT b.`city`)
Well, I think we should have waited for you to show your own query, because one learns best from mistakes and their explanations. However, now that you've got answers already:
Yes, you need group by and count. I'd group by cities to see if I got duplicates. Then select countries and exclude those that have duplicate cities.
select *
from country
where id not in
(
select country_id
from businessmen
group by city, country_id
having count(*) > 1
);
You need either nested aggregations:
select *
from Country
where id in
(
select country_id
from
(
select city, country_id,
count(*) as cnt -- get the number of rows per country/city
from Businessman
group by city, country_id
) as dt
group by country_id
having max(cnt) = 1 -- return only those countries where all counts are unique
)
Or compare two counts:
select *
from Country
where id in
(
select country_id
from Businessman
group by country_id
having count(*) = count(distinct city) -- number of cities is equal to umber of rows
)
I want to join multiple queries with different tables and column name, along with I need to display the count of duplicate fields as shown below.
The queries are: (Proj_uid is common in all the tables which I need to match)
select proj_name,Agency,District,Division,Proj_status from tempproj
Need to join 2 tables to get the result that is payment80 and payment20 which contains billtype column with duplicate values, I want to count those value too
SELECT p.Proj_name,p.billtype, COUNT(1) as CNT
FROM payment80 p where billtype='civil'
GROUP BY Proj_name, billtype
(This is by using single table but I want this result by joining both payment80 and payment20 tables)
SELECT p.Proj_name,p.billtype, COUNT(1) as CNT
FROM payment80 p where billtype='Electric'
GROUP BY Proj_name, billtype
(The billtype values I want to count and just display a number of duplicate records)
Proj_Name billtype
------------------------
policegruha civil
gruhayojna Electric
policegruha civil
dcoffice civil
spoffice Electric
dcoffice civil
3) Select billtype from payment, here also I need count the duplicate values and display in billtype
Duplicate values will be in billtype which contains some thing like this:
Finally I want an output like this:
Proj_name Agency District Division Projstatus Civilbilltype Electricbilltype
policegruha kumar chitradurga davangere ongoing 3 1
gruhayojna khan ballary ballary completed 2 2
Atered john bangalore bangalore ongoing 2 4
dcoffice ravi mangalore mangalore ongoing 1 2
spoffice mary chitradurga davangere completed 3 4
hostel jack ballary ballary completed 3 3
univercity kumar bangalore bangalore ongoing 4 2
mess Raj mysore mysore ongoing 2 1
policestation khan mysore mysore ongoing 1 4
conferencehall Rosy davangere davangere ongoing 2 2
You are joining three separate tables. One is physical, tempproj, and the other two are virtual: they are aggregates.
This is the technique.
SELECT p.proj_name,p.Agency,p.District,p.Division,p.Proj_status,
Civilbills.billcount as Civilbills,
Electribills.billcount as Electricbills
FROM tempproj p
LEFT JOIN (
SELECT Proj_name, COUNT(*) as billcount
FROM payment80
where billtype='civil'
GROUP BY Proj_name
) Civilbills ON Civilbills.Proj_name = p.proj_name
LEFT JOIN (
SELECT Proj_name, COUNT(*) as billcount
FROM payment80
where billtype='Electric'
GROUP BY Proj_name
) Electricbills ON Electricbills.Proj_name = p.proj_name
Your requirement includes two separate aggregates from the payment80 table. The LEFT JOINs prevent suppression of project rows that lack any bills of either category.
I am working on a project where users work on reports and enter details of their work in database. My database structure has two tables:
tbl_reports - this table contains all details of work performed
report_id user_id date country status
-----------------------------------------------------------------------
0001 abc 2014-05-04 USA checked
0002 abc 2014-05-04 USA checked
0003 abc 2014-05-05 India checked
0004 lmn 2014-05-04 USA checked
0005 lmn 2014-05-04 India checked
0006 xyz 2014-05-06 Taiwan checked
tbl_time - this table contains all details on time repoted by the users, date and country wise
id user_id date country time (hrs)
----------------------------------------------------
01 abc 2014-05-04 USA 4
02 abc 2014-05-05 India 2
03 lmn 2014-05-04 USA 3
04 lmn 2014-05-04 India 2
05 opq 2014-05-05 Belgium 4
As you can see users "abc and "lmn" have tracked all their tasks appropriately while user "xyz" has not tracked his time yet and user "opq" has tracked his time but has no records of reports he has worked on.
Now out of this I want to extract details of this team GROUPING BY "date" and "country" as below:
date country total_report_count total_time_count
-----------------------------------------------------------------------
2014-05-04 India 1 2
2014-05-04 USA 3 7
2014-05-05 Belgium 0 4
2014-05-05 India 1 2
2014-05-06 Taiwan 1 0
Which means irrespective of which user has tracked his reports or time, I need to generate team report for worked done in which country on which date , its counts and total time tracked.
Now I was able to find total_time_count report using below code:
CREATE VIEW vw_teamreport AS
SELECT
tb1.date , tb1.country,
SUM(tb1.time) AS total_time_count
FROM tbl_time tb1
LEFT JOIN tbl_reports tb2
ON tb2.report_id IS NULL
GROUP BY tb1.date, tb1.country
ORDER BY tb1.date, tb1.country;
Need help to complete the problem, and I am using MYSQL (In case if FULL JOIN is required, FULL JOIN keyword is not supported)
Because there's no FULL JOIN you'll need a query to pull out all the distinct date/country combinations from the UNION of these two tables. Or, you'll need some other query to generate the full list of dates and countries. Call this query A.
You need to write two separate aggregating queries. One will aggregate the hours by date and country and the other will aggregate the reports by date and country. Call these queries B and C.
Then you need to do
SELECT whatever, whatever
FROM (
/*query A*/
) AS a
LEFT JOIN (
/*query B*/
) AS b ON a.date=b.date AND a.country=b.country
LEFT JOIN (
/*query C*/
) AS c ON a.date=c.date AND a.country=c.country
This will produce a correctly summarized report with all the rows you need, and NULLs where there is missing summary data.
Edit
Sorry, forgot about the nested query view restriction. You'll need to create four views, one for each subquery and one for the join query. So it will be:
CREATE VIEW dates_countries AS
SELECT DISTINCT `date`, country FROM tbl_time
UNION
SELECT DISTINCT `date`, country FROM tbl_reports;
CREATE VIEW time_totals AS
SELECT `date`, country, SUM(time) AS tot
FROM tbl_time
GROUP BY `date`, country
CREATE VIEW report_totals AS
SELECT `date`, country, COUNT(*) AS tot
FROM tbl_reports
GROUP BY `date`, country
And finally this view.
CREATE VIEW team_report AS
SELECT a.`date`, a.country,
c.tot AS total_report_count,
b.tot AS total_time_count
FROM dates_countries AS a
LEFT JOIN time_totals AS b ON a.`date` = b.`date` AND a.country = b.country
LEFT JOIN repoorts_totals AS r ON a.`date` = r.`date` AND a.country = r.country;
You don't have much choice about this when you need a view.
you could do a divide it into two sub-queries with each section providing one of the reporting data like this,
CREATE VIEW vw_teamreport AS
SELECT tt.date, tt.country, t1.total_time_count, t2.total_report_count
FROM
(SELECT distinct tb1.date , tb1.country
FROM tbl_time tb1
UNION
SELECT tb1.date , tb1.country
FROM tbl_reports tb1
) tt
LEFT JOIN
(SELECT tb1.date , tb1.country, SUM(tb1.time) AS total_time_count
FROM tbl_time tb1
GROUP BY tb1.date, tb1.country) t1
ON tt.date = t1.date and tt.country = t1.country
LEFT JOIN
(SELECT tb1.date , tb1.country, COUNT(tb1.country) AS total_report_count
FROM tbl_reports tb1
GROUP BY tb1.date, tb1.country)t2
ON tt.date = t2.date and tt.country = t2.country
The first query provides the union for all time & country. The 2nd and the 3rd query provides the report data.
I'm wondering if there is a way to perform this resulting query in Microsoft Access (2010) without writing VBA code.
I have a data table like this (more fields, but only showing the ones that count):
Customer ID City Name
1 Chicago
2 Chicago
3 New York
4 San Antonio
5 Seattle
6 Seattle
7 Walbash
8 Walbash
I want to only select one row with each City. For example, I want to select either row 1 or 2 for Chicago.
Resulting Results would look similar to this
Customer ID City Name
1 Chicago
3 New York
4 San Antonio
etc
Try something like this :
select *
from Table1 t1
where t1.CustomerID = (select min(t2.CustomerID) from table1 t2 where t1.CityName = t2.CityName)
order by t1.CustomerID
SELECT First(Table1.CustomerID) AS FirstOfCustomerID, Table1.CityName
FROM Table1
GROUP BY Table1.CityName;
Example of other options to using First() are Min(), Max()
Edit:
Create the above query then do a join with your original table to select the record. I named the above query Q1 and the original table is Table1
SELECT Q1.FirstOfCustomerID, Q1.CityName, Table1.Data
FROM Q1 INNER JOIN Table1 ON Q1.FirstOfCustomerID = Table1.CustomerID;
Please help. I'm using MySQL 5.1.30 Community Edition.
I have four tables: nts, operator, country, cooperationtype
table `nts` has one column(`operatorId`) which is a foreign key to column `id` in table `operator` and one column(`voice`) which is a foreign key to column `id` in table cooperationtype
table operator has one column(`country_id`) which is a foreign key to column (`id`) in table country
I want to get the counts of operators and countries where all of the value of voice not equals to 'N/A' and grouped them by cooperationtype.id with this query:
SELECT cooperationtype.id AS cooptype,
COUNT(DISTINCT country_id) AS country, COUNT(DISTINCT operatorId) AS operator
FROM nts INNER JOIN operator ON operator.id = nts.operatorId INNER JOIN country ON operator.country_id = country.id
INNER JOIN cooperationtype ON cooperationtype.id = nts.voice
WHERE cooperationtype.code <> 'N/A' GROUP BY cooperationtype.id
I got this result:
cooptype country operator
1 128 348
2 11 11
3 15 17
The sum of this query is 154 countries and 376 operators.
But then when I want to get all of the counts of operators and countries where all of the value of voice not equals to 'N/A', regardless the of cooperationtype.id with this query:
SELECT COUNT(DISTINCT country_id) AS country, COUNT(DISTINCT operatorId) AS operator
FROM nts INNER JOIN operator ON operator.id = nts.operatorId INNER JOIN country ON operator.country_id = country.id
INNER JOIN cooperationtype ON cooperationtype.id = nts.voice
WHERE cooperationtype.code <> 'N/A'
I got this result:
country operator
133 372
My questions are:
Why is the sum of the result from the first query doesn't equal to the result from the second query?
Which one is the right result?
Data example:
voice country operator
1 US 1
1 US 2
1 UK 3
1 UK 4
2 US 1
2 US 2
For the first query, the data should generate:
cooptype country operator
1 2 4
2 2 2
For the second query, the data should generate:
country operator
2 4
Why is the sum of the result from the first query doesn't equal to the result from the second query?
Because you use COUNT(DISTINCT).
It counts distinct records group-wise.
Your first query counts two records with the same country but different cooptype twice (since it groups by cooptype), while the second one counts them once.
Which one is the right result?
Both are right.
For the given data:
cooptype country
1 US
1 US
1 UK
1 UK
2 US
2 US
the first query will return:
1 2
2 1
and the second will return
2
, since you have:
2 distinct countries in cooptype = 1 (US and UK)
1 distinct country in cooptype = 2 (US)
2 distinct countries overall (US and UK)
Which is "right" in your definition of "right", depends, well, on this definition.
If you just want the second query to match the results of the first one, use
SELECT COUNT(DISTINCT cootype, country_id) AS country,
COUNT(DISTINCT cooptype, operatorId) AS operator
FROM nts
INNER JOIN
operator
ON operator.id = nts.operatorId
INNER JOIN
country
ON operator.country_id = country.id
INNER JOIN
cooperationtype
ON cooperationtype.id = nts.voice
WHERE cooperationtype.code <> 'N/A'
but, again, this may be as wrong as your first query is.
For these data:
cooptype country operator
1 US 1
1 US 1
1 UK 2
1 UK 2
2 US 1
2 US 1
, what would be a correct resultset?