Return data from several tables in database - mysql

I need some help getting data from several tables.
This is the tables I have:
_______________ ______________ ___________ _______________ _____________
|_tblUsers____| |_tblAnswers__| |_tblAlt__| |_tblQuestion_| |_survey_____|
| userID | | answerAltID | | altID | | questID | | surveyID |
| username | | userID | | altText | | questText | | surveyName |
|_____________| |_____________| |_questID_| |_surveyID____| |____________|
TblUsers have a list of users in the system, tblAnswers have all answers that has been given from users, tlbAlt holds alternatives for a question, and tblQuestion has the questions. There is one more table called tblSurveys, but that's not needed here as the ID is mentioned in the tblQuestion.
This is what I have so far:
SELECT
tblQuestion.questText,
tblAlt.altText,
Count(tblAnswers.answerID) as answers_count,
(SELECT COUNT(answerID) FROM tblAnswers, tblAlt
WHERE tblAnswers.answerAltID = tblAlt.altID
AND tblAlt.questID = " & CInt(questionID) & ") as total_count
FROM tblAlt, tblQuestion
LEFT JOIN tblAnswers ON (tblAlt.altId = tblAnswers.altID)
WHERE tblAlt.questID = " & CInt(questionID) & "
GROUP BY tblAlt.altText;
This returns rows like this:
| What is blablabla? | The answer is... | 2 (has answered) | 10 (total
answers) |
This unfortunately only returns all rows for one question. Is there a way to get all rows that is a part of same survey (based on surveyID)?
If want the output to be like this:
| What is blablabla? | The answer is... | 2 (has answered) | 10 (total
answers) | Name of Survey |
I want to return ALL alternatives (with how many answered, total answers, related question and survey).
Update:
This my input:
SELECT tblalternativ.altTekst, tblalternativ.altID, Count(tblsvar.svarAltID) as antSvar,
(SELECT COUNT(*) FROM tblsvar, tblalternativ
WHERE tblsvar.svarAltID = tblalternativ.altID
AND tblalternativ.altSpmID = " & CInt(lblQuestion.Tag) & ") as antTotal,
(SELECT Count(*) FROM tblalternativ WHERE altSpmID = " & CInt(lblQuestion.Tag) & ") as spmTotal
FROM(tblalternativ) LEFT JOIN tblsvar ON (tblalternativ.altId = tblsvar.svarAltID)
WHERE(tblalternativ.altSpmID = " & CInt(lblQuestion.Tag) & ")
GROUP BY tblalternativ.altTekst ORDER BY tblalternativ.altID ASC
My output:
altTekst altID antSvar antTotal spmTotal
Black 83 1 3 5
Green 84 1 3 5
Yellow 85 1 3 5
White 86 0 3 5
Pink 87 0 3 5
But this only show statistics for one question. I want to show for all questions in one survey. So I need to get all altTekst for that survey, question name, and ID of survey.
I want:
spmTekst altTekst altID antSvar antTotal spmTotal evalID
What is... Black 83 1 3 5 1
What is... Green 84 1 3 5 1
What is... Yellow 85 1 3 5 1
What is... White 86 0 3 5 1
What is... Pink 87 0 3 5 1
Who is.... The king 88 2 3 3 1
Who is.... The pope 89 0 3 3 1
Who is.... The president 90 1 3 3 1
Which.... Shoe 91 2 3 2 1
Which.... Hat 92 1 3 2 1
In other words, I want the statistics from all questions in same survey (based on evalID).

To return all questions, answer text, count ofusers with that answer, and total answers provided; per survey.
Select TQ.QuestText, tAlt.altText, count(*) as Answers_count, suM(mTemp.Cnt) as total_count
FROM tblQuestion tq
LEFT JOIN tblAlt talt on Talt.QuestID = TQ.QuestID
LEFT JOIN tblAnswers ta on ta.AnswerAltID = talt.AltID
LEFT JOIN tblUsers tu ON Ta.UserID = TU.UserID
LEFT join tblAnswers ta2 on ta2.answeraltId = talt.altID
LEFT JOIN
(SELECT COUNT(*) cnt, questID
FROM tblAnswers
INNER JOIN tblAlt on AltID = AnswerAltID
group by questID) mTemp
on mTemp.QuestID = talt.QuestID
WHERE tQ.SurveyID = 123 --Change this to your value
Group by TQ.QuestText, TAlt.AltText
It's just a bunch of left joins vs inner joins; and i abstracted out the counts for each table once so it should be faster instead of doing a sub select on each row. This way it does it once for all rows and is done.

Try this(Not at all optimized, just added the survey part to it):
SELECT tblQuestion.questText, tblAlt.altText,
Count(tblAnswers.answerAltID) AS answers_count,
(SELECT COUNT(answerAltID) FROM tblAnswers, tblAlt
WHERE tblAnswers.answerAltID = tblAlt.altID AND
tblAlt.questID = " & CInt(questionID) & ") as total_count,
survey.surveyName
FROM survey, tblQuestion, tblAlt
LEFT JOIN tblAnswers ON (tblAlt.altId = tblAnswers.answerAltID)
WHERE tblAlt.questID = " & CInt(questionID) & " AND
tblQuestion.surveyID = survey.surveyID
GROUP BY tblAlt.altText;
Edit: Try this then:
SELECT tblQuestion.questText AS spmTekst, tblAlt.altText AS altTekst,
tblAlt.altID,
Count(tblAnswers.answerAltID) AS antSvar,
COUNT(tblAlt.altID) AS antTotal,
COUNT(tblQuestion.questID) AS spmTotal,
survey.surveyID AS evalID
FROM tblQuestion
JOIN survey ON (survey.surveyID = tblQuestion.surveyID)
JOIN tblAlt ON (tblAlt.questID = tblQuestion.questID)
LEFT JOIN tblAnswers ON (tblAnswers.answerAltID = tblAlt.altID)
WHERE tblAlt.questID = " & CInt(questionID) & " AND -- what really is this? review this
survey.surveyID = '123' -- the value u want
GROUP BY tblAlt.altText
ORDER BY tblAnswers.answerAltID;

Related

Find duplicates from same table and constraint them from another table in sql

Oh, my title is not the best one and as English is not my main language maybe someone can fix that instead of downvoting if they've understood the issue here.
Basically i have two tables - tourneyplayers and results. Tourneyplayers is like a side table which gathers together tournament information across multiple tables - results, tournaments, players etc. I want to check duplicates from the results table over column day1_best, from single tournament and return all the tourneyplayers who have duplicates.
Tourneyplayers contain rows:
Tourneyplayers
tp_id | resultid | tourneyid
1 | 2 | 91
2 | 21 | 91
3 | 29 | 91
4 | 1 | 91
5 | 3 | 92
Results contains rows:
Results:
r_id | day1_best
1 | 3
2 | 1
3 | 4
.. | ..
21 | 1
.. | ..
29 | 2
Now tourney with id = 91 has in total 4 results, with id's 1,2,21 and 29. I want to return values which have duplicates, so currently the result would be
Result
tp_id | resultid | day1_best
1 | 2 | 1
2 | 21 | 1
I tried writing something like this:
SELECT *
FROM tourneyplayers
WHERE resultid
IN (
SELECT r1.r_id
FROM results AS r1
INNER JOIN results AS r2 ON ( r1.day1_best = r2.day1_best )
AND (
r1.r_id <> r2.r_id
)
)
AND tourneyid =91
But in addition to values which had the same day1_best it chose two more which did not have the same. How could i improve my SQL or rewrite it?
First you JOIN both tables, so you know how the data looks like.
SELECT *
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`;
Then using the same query you GROUP to see what tourneyid, day1_best combination has multiple rows
SELECT `tourneyid`, `day1_best`, count(*) as total
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`
GROUP BY `tourneyid`, `day1_best`;
Finally you use the base JOIN and perform a LEFT JOIN to see what rows has a match and show only those rows.
SELECT t.`tp_id`, r.`r_id`, r.`day1_best`
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`
LEFT JOIN (SELECT `tourneyid`, `day1_best`, count(*) as total
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`
GROUP BY `tourneyid`, `day1_best`
HAVING count(*) > 1) as filter
ON t.`tourneyid` = filter.`tourneyid`
AND r.`day1_best` = filter.`day1_best`
WHERE filter.`tourneyid` IS NOT NULL;
SQL DEMO
OUTPUT
Please try this :
Select tp.tp_id , tp.resultid ,r.day1_best from (Select * from Tourneyplayers
where tourneyid = 91)as tp inner join (select * from Result day1_best in(select
day1_best from result group by day1_best having count(*)>1 ) )as r on tp.resultid
= r.r_id ;

Select on 2 tables return no result?

I have 2 tables :
Table 'annonce' (real estate ads) :
idAnnonce | reference
-----------------------
1 | dupond
2 | toto
Table 'freeDays' (Free days for all ads) :
idAnnonce | date
-----------------------
1 | 2015-06-06
1 | 2015-06-07
1 | 2015-06-09
1 | 2015-06-10
2 | 2015-06-06
2 | 2015-06-07
2 | 2015-06-12
2 | 2015-06-13
I want to select all alvailable ads who have only free days between a start and end date, I have to check each days between this date.
The request :
SELECT DISTINCT
`annonce`.`idAnnonce`, `annonce`.`reference`
FROM
`annonce`, `freeDays`
WHERE
`annonce`.`idAnnonce` = `freeDays`.`idAnnonce`
AND
`freeDays`.`date` = '2015-06-06'
AND
`freeDays`.`date` = '2015-06-07'
Return no result. Where is my error ?
It cant be equal both dates
SELECT DISTINCT a.idAnnonce, a.reference
FROM annonce a
INNER JOIN freeDays f ON a.idAnnonce = f.idAnnonce
WHERE f.date BETWEEN '2015-06-06' AND '2015-06-07'
What Matt is say is correct. You can also do this as alternative:
SELECT DISTINCT a.idAnnonce, a.reference
FROM annonce a
INNER JOIN freeDays f ON a.idAnnonce = f.idAnnonce
WHERE f.date IN('2015-06-06','2015-06-07')
Or like this:
SELECT DISTINCT a.idAnnonce, a.reference
FROM annonce a
INNER JOIN freeDays f ON a.idAnnonce = f.idAnnonce
WHERE f.date ='2015-06-06' OR f.date ='2015-06-07'
This will give you the same result as with an BETWEEN
Your WHERE clause is asking for the impossible!
You are asking for rows where the 'freedays'.'date' value is both 2015-06-06 and 2015-06-07.
AND
freeDays.date = '2015-06-06'
AND
freeDays.date = '2015-06-07'
You need to use BETWEEN:
freeDays.date BETWEEN '2015-06-06' AND '2015-06-07'
AND
freeDays.date = '2015-06-06'
OR
freeDays.date = '2015-06-07'

SQL select from 2 tables with restrictions

Basically, I have a two tables: management and pictures.
I need a list with all properties from the management table. If pictures exist, the list only has to show the first picture (seq=1).
My problem: the list doesn't show the properties without pictures. How do I solve this?
I have the following tables:
tblMan
propid | city | lang | ...
1 Mol NL
2 Olen NL
3 Geel NL
4 Ham FR
tblPic
propid | lang | seq | filename
1 NL 1 file11
1 FR 1 file12
1 NL 2 file13
1 FR 2 file14
1 NL 3 file15
1 FR 3 file16
3 NL 22 file17
3 FR 22 file18
3 NL 23 file19
3 FR 23 file20
3 NL 24 file21
3 FR 24 file22
SELECT m.propid, m.city, p.filename
FROM tblMan as m
INNER JOIN tblProp as p ON m.propid = p.propid
WHERE m.lang = 'NL' AND p.lang = 'NL' AND p.picture_index = '1'
The result is:
propid | city | filename
1 Mol file11
3 Geel file17
He doesn't get the propid 2 (Olen) because there is no image!
I want the result:
propid | city | filename
1 Mol file11
2 Olen (Null or 0 or...)
3 Geel file17
I tried subquery, union, concat... (#_#) with no result.
Does anybody has an idee what kind of SQL statement I should use?
Thanks for helping!
use LEFT JOIN since you want to display any records on table tblMan that matches your condition on the WHERE clause and move some filtering condition for table tblProp on the ON clause
SELECT m.propid,
m.city,
p.filename
FROM tblMan as m
LEFT JOIN tblProp as p
ON m.propid = p.propid
AND p.lang = 'NL'
AND p.picture_index = '1'
WHERE m.lang = 'NL'
Try this:
SELECT m.propid,
m.city,
p.filename
FROM tblman AS m
LEFT JOIN tblprop AS p
ON m.propid = p.propid
AND p.lang = 'NL'
AND p.picture_index = '1'
WHERE m.lang = 'NL'
You need to use LEFT OUTER JOIN in place of INNER JOIN.

difficulties getting a 3 table join to return expected results

I'm having some difficulty getting to the bottom of this sql query.
Tables:
--Tickets-- --Finance-- --Access--
id_tickets id_finance id_access
name_tickets id_event id_event
cat_tickets id_tickets id_tickets
sold_finance scan_access
Finance and Access both contain a row for multiple of each ticket type as listed in tickets.
and I'm trying to get:
cat_tickets | total_sold | total_scan
-------------------------------------
single | 3043 | 2571
season | 481 | 292
comp | 114 | 75
-------------------------------------
total | 3638 | 2938
The closest I've been to the result I've used:
SELECT tickets.cat_tickets, COALESCE(SUM(finance.sold_finance), 0) AS total_sold, COALESCE(SUM(access.scan_access), 0) AS total_scan
FROM finance INNER JOIN tickets ON finance.id_tickets = tickets.id_tickets
INNER JOIN access ON access.id_tickets = tickets.id_tickets
WHERE access.id_event = 235 AND finance.id_event = access.id_event
GROUP BY tickets.cat_tickets
ORDER BY tickets.cat_tickets DESC
but that just returns:
cat_tickets | total_sold | total_scan
-------------------------------------
single | 4945 | 4437
season | 954 | 599
comp | 342 | 375
-------------------------------------
total | 6241 | 5411
Any ideas where I could be going wrong?
Thanks!
The problem is the relation between access and finance tables, you have to join them. Even if you LEFT JOIN the table the predicate finance.id_event = access.id_event will make it INNER JOIN. As a work around, use UNION like this:
SELECT
tickets.cat_tickets,
SUM(CASE WHEN a.Type = 'f' THEN num ELSE 0 END) AS total_sold,
SUM(CASE WHEN a.Type = 'a' THEN num ELSE 0 END) AS total_scan
FROM tickets
LEFT JOIN
(
SELECT 'f' Type, id_tickets, sold_finance num
FROM finance f
WHERE id_event = 1
UNION ALL
SELECT 'a', id_tickets, scan_access
FROM access
WHERE id_event = 1
) a ON a.id_tickets = tickets.id_tickets
GROUP BY tickets.cat_tickets;
SQL Fiddle Demo
Although I am fully clear on what you want, just try this query if the result of this is what you are expecting.
SELECT tickets.cat_tickets, COALESCE(SUM(finance.sold_finance), 0) AS total_sold, COALESCE(SUM(access.scan_access), 0) AS total_scan
FROM finance LEFT JOIN tickets ON finance.id_tickets = tickets.id_tickets
LEFT JOIN access ON access.id_tickets = tickets.id_tickets
WHERE access.id_event = 235
GROUP BY tickets.cat_tickets
ORDER BY tickets.cat_tickets DESC
Disclaimer: This query is not tested due to incomplete data on the question.
SELECT z.Cat_tickets,
COALESCE(x.total_sold,0) total_sold,
COALESCE(y.total_scan,0) total_scan
FROM tickets z
LEFT JOIN
(
SELECT a.id_tickets,
a.cat_tickets,
SUM(b.sold_finance) total_sold
FROM tickets a
INNER JOIN finance b
ON a.id_tickets = b.id_tickets
WHERE id_event = 235
GROUP BY a.id_tickets, a.cat_tickets
) x ON z.id_tickets = x.id_tickets
LEFT JOIN
(
SELECT aa.id_tickets,
aa.cat_tickets,
SUM(bb.scan_access) total_scan
FROM tickets aa
INNER JOIN Access bb
ON aa.id_tickets = bb.id_tickets
WHERE id_event = 235
GROUP BY aa.id_tickets, aa.cat_tickets
) y ON z.id_tickets = y.id_tickets

Getting JOIN and WHERE to work together

Ok, I have an example table with the following information and query.
First up is the data, with the question following at the end.
Here's the SQL Dump:
http://pastie.org/private/o7zzajdpm6lzcbqrjolgg
Or you can use the included a visual below:
Purchases Table
| id | brand | date |
1 b1 2000-01-01
2 b1 2000-01-03
3 b2 2000-01-04
4 b3 2000-01-08
5 b4 2000-01-14
Owners Table
id | firstname | lastname | purchaseid | itemCoupon | itemReturned | Accessories
1 Jane Doe 1 yes no 4
2 Jane Doe 2 yes no 2
3 Jane Doe 3 no no 1
4 Jane Doe 4 no no 3
5 Jane Doe 5 no yes 6
The Query
SELECT brand, COALESCE( SUM( inTime.Accessories ) , 0 ) AS acessory_sum
FROM purchases
INNER JOIN owners AS person ON person.purchaseid = purchases.id
AND person.firstname = 'Jane'
AND person.lastname = 'Doe'
LEFT JOIN owners AS inTime ON person.id = inTime.id
AND purchases.date
BETWEEN DATE( '2000-01-01' )
AND DATE( '2000-01-05' )
GROUP BY purchases.brand
This gives the following expected result:
| brand | accessory_sum
b1 6
b2 1
b3 0
b4 0
The question
Now, I would like to add to the query:
WHERE itemCoupon = 'yes' OR itemReturned = 'yes'
But this overrides the last join and when I do the same search above I get:
| brand | accessory_sum
b1 6
b2 1
Similarly I still want it to return No results found for 2000-01-04, 2000-01-08 using WHERE itemCoupon = 'yes' OR itemReturned = 'yes'. Removing the WHERE gives me zeros for all brands if I try to do it another way.
Basically I want to keep the way the WHERE behaves but also keep the format that I described in the first example of the expected output.
As it is now, using WHERE destroys the way the last LEFT JOIN works with COALESCE which fills the remaining brand rows with zeros.
Your WHERE turns the outer join into an inner join.
You need to move your additionally condition into the LEFT JOIN condition:
LEFT JOIN owners as inTime
ON person.id = inTime.id
AND purchases.date between purchases.date DATE ('2000-01-01') and DATE ('2000-01-05')
AND (inTime.itemCoupon = 'yes' or inTime.itemReturned = 'yes')
the ON clause when doing a JOIN is similar to the WHERE clause. So instead of trying to use WHERE, just add another AND to your query (and don't forget to use the parenthesis in the OR clause):
SELECT brand,
COALESCE(SUM(Time.purchasedAccessories),0) as acessory_sum
FROM purchases
INNER JOIN owners AS person
ON person.purchaseid = purchases.id
AND person.firstname = 'Jane'
AND person.lastname = 'Doe'
AND (person.itemCoupon = 'yes' OR person.itemReturned = 'yes')
LEFT JOIN owners AS inTime
ON person.id= inTime.id
AND purchases.date
BETWEEN purchases.date
DATE( '2000-01-01' )
AND
DATE( '2000-01-05' )
GROUP BY purchases.brand