I have a MySQL table
"ratings", with
an ID column
a column called like_dislike (holds either null, 0, or 1),
and a column called lesson_id (a foreign key from lessons).
the MySQL table, "lessons", with
an ID column
a teacher_id column
I need to select this information:
"SELECT r.like_dislike FROM ratings r INNER JOIN lessons l on l.lesson_id = r.lesson_id";
However, this actually part of a much larger SQL statement, and what I would like to do is:
Foreach lesson_id, if like_dislike == 0, SELECT count(like_dislike) as like
AND
Foreach lesson_id, if like_dislike == 1, SELECT count(like_dislike) as dislike
I do not know how to turn this pseudo code into SQL. I also need to do this in SQL, rather than in something like PHP, because it is part of a larger SQL statement whose conversion into properly formatted arrays is deeply troubling.
You should be able to accomplish this with grouping. For example:
SELECT r.lesson_id, COUNT(*) AS like
FROM ratings r
INNER JOIN lessons l ON l.lesson_id = r.lesson_id
WHERE r.like_dislike = 0
GROUP BY r.lesson_id;
The same for dislike, just change the WHERE clause to
WHERE r.like_dislike = 1
EDIT:
This can be combined into one query as requested, by adding another level of grouping:
SELECT r.lesson_id, r.like_dislike, COUNT(*) AS count
FROM ratings r
INNER JOIN lessons l ON l.lesson_id = r.lesson_id
GROUP BY r.lesson_id, r.like_dislike;
This will give you output, for example:
+-----------+--------------+-------+
| lesson_id | like_dislike | count |
+-----------+--------------+-------+
| 1 | 0 | 12 |
| 1 | 1 | 7 |
| 2 | 0 | 1 |
| 2 | 1 | 4 |
+-----------+--------------+-------+
so for lesson_id of 1, there are 12 likes, and 7 dislikes, etc...
EDIT 2:
To get one row for each lesson_id, you can modify the statement a little:
SELECT r.lesson_id,
CASE WHEN r.like_dislike = 0 THEN COUNT(*) END AS like,
CASE WHEN r.like_dislike = 1 THEN COUNT(*) END AS dislike
FROM ratings r
INNER JOIN lessons l ON l.lesson_id = r.lesson_id
GROUP BY r.lesson_id, r.like_dislike;
For that matter, you don't even need to join on the lessons table at all, unless you are somehow getting ratings that do not link to a lesson. If you want to include lessons that have no ratings, then you will have to change to an OUTER join:
SELECT l.lesson_id,
CASE WHEN r.like_dislike = 0 THEN COUNT(*) END AS like,
CASE WHEN r.like_dislike = 1 THEN COUNT(*) END AS dislike
FROM lessons l
LEFT JOIN ratings r ON r.lesson_id = l.lesson_id
GROUP BY l.lesson_id, r.like_dislike;
Maybe you can use a case statement, something like this:
SELECT r.lesson_id,
case when r.like_dislike == 0 then (count(*)) end as like,
case when r.like_dislike == 1 then (count(*)) end as dislike
FROM ratings r INNER JOIN lessons l ON l.lesson_id = r.lesson_id
GROUP BY r.lesson_id, r.like_dislike
I haven't tested it, but you can see the idea. Further, you must set a case to count 1's and another to count 0's because like_dislike can be null.
try this
SELECT if(r.like_dislike =0 ,count(like_dislike) as like , if( r.like_dislike =1 , count(like_dislike) as dislike, 'its null'))
FROM ratings r
INNER JOIN lessons l on l.lesson_id = r.lesson_id
if you are adding condition on yr datatable then code it like below:
Declare #Counter int
Set #Counter=(SELECT Count(Student_ID) as 'StudentCount' FROM tbCourseSemOne
where Student_ID=1 Having Count(Student_ID) < 6 and Count(Student_ID) > 0)
if(#Counter <6)
print'Sorry! You cannot add more than five subject data for a single stduent'
else
print'Insert Code'
hope it helps
Related
I'm trying to create an effective query but can't get it working.
Tables:
- one table containing types of objects
- one table containing objects
Conditions:
- there can be single objects of a type
- there can be child objects of a type
- parent and child objects don't need to be of the same type
- objects can be published
- types can be published
- the results should only get pulled from a specific pool of object IDs. So i need to add AND (o.id IN (1,2,3,4)
I want a simple result list that shows how many types are published and the number of objects assigned to these types.
types
id | title | published
---------------------
1 type1 1
2 type2 1
3 type3 1
4 type4 1
5 type5 1
6 type6 0
7 type7 1
objects
id |title | type | parent | published
---------------------------------------
1 a 1 0 1
2 b 1 0 1
3 c 3 2 1
4 d 2 0 1
5 e 2 2 1
6 f 4 0 0
7 g 5 6 1
8 h 6 0 1
9 i 3 8 1
10 j 3 8 0
11 k 7 8 1
Results should be:
type1 (#2) (two singles)
type2 (#2) (one single + one child of id 2)
type3 (#3) (one child of id 2 + one published child of id 8)
type4 (#0) (one single not published)
type5 (#0) (because it's parent id 6 is not published)
type6 (#0) (because type6 is not published)
I tried this one (type publishing not included):
SELECT o.type, t.title, COUNT(t.id) AS cnt
FROM types AS t
LEFT JOIN objects AS o ON o.type = t.id
LEFT JOIN objects AS o2 ON o.id = o2.parent
WHERE o.published = 1 AND o2.published = 1
GROUP BY o.type
The conditions in the WHERE clause negate the "outerness" of the left joins.
Move those conditions to the ON clauses. The WHERE clause can be dropped.
Also, reference columns from t, the driving table, and count non-NULL expressions from the outer joined tables.
That will allow the query to return zero counts.
I didn't fully delve into the specification, but it looks like we want to count matching rows from o and o2.
I think something like this will get a resultset consistent with one interpretation of the specification... child o2 rows get counted under parent o type, regardless of the type on the child o2 row.
This is not tested, and I'm not fully understanding the specification...
SELECT t.id AS `type`
, t.title AS `title`
, COUNT(DISTINCT o.id)
+ COUNT(DISTINCT o2.id) AS `cnt`
-- , COUNT(DISTINCT o.id) AS `cnt_o`
-- , COUNT(DISTINCT o2.id) AS `cnt_o2`
FROM types t
LEFT
JOIN objects o
ON o.type = t.id
AND o.published = 1
AND o.parent = 0
AND t.published = 1
LEFT
JOIN objects o2
ON o2.parent = o.id
AND o2.published = 1
GROUP
BY t.id
, t.title
Not clear in the spec...
Do child rows (from o2) get omitted from the count if the type on the o2 row matches a row in types that is published=0 ?
If we are "grouping" by type on the o2 rows , then we'd need to something different,
EDIT
we could get the count from the parent and the child separately, in two separate SELECT, and then combine the two resultsets with a UNION ALL set operator, and then total up the counts.
something along these lines:
SELECT c.type
, c.title
, SUM(c.cnt) AS cnt
FROM (
SELECT t.id AS `type`
, t.title AS `title`
, COUNT(o.id) AS `cnt`
FROM types t
LEFT
JOIN objects o
ON o.type = t.id
AND o.published = 1
AND o.parent = 0
AND t.published = 1
GROUP
BY t.id
, t.title
UNION ALL
SELECT tc.id AS `type`
, tc.title AS `title`
, COUNT(oc.id) AS `cnt`
FROM types tc
JOIN objects oc
ON oc.type = t.id
AND oc.published = 1
AND t.published = 1
JOIN objects op
ON op.id = oc.parent
AND op.published = 1
JOIN types pt
ON pt.id = op.type
AND pt.published = 1
GROUP
BY tc.id
, tc.title
) c
GROUP
BY c.type
, c.title
again, untested, and without a full understanding of the spec.
the count of the parent o is straightforward. we use an outer join, with t as the driving table, so we get all types, and can get zero counts.
the count of the child oc, we can do inner joins. since the previous SELECT is getting us all the types, missing rows in the second SELECT won't cause a problem.
note that we join the child o2 rows by type, and then we join to parent (to make sure parent is published), and join to parent type (to check that type is published) ...
How do we distinguish "parent" rows, do we check parent=0 ?
Is this a hierarchy, can a "child" also be the "parent" of another row ?
FOLLOWUP
Another way to think about it (maybe this was the approach of the OP query) ... we are counting rows from o, parents and children. What's important is that the type is published type, and that o is published.
Additionally, either
o is not a child (i.e. there isn't a row in objects op that has an id value equal to `o.parent)
or
if o does have a parent row (a row in objects op with an id value equal to o.parent, the [parent op is published and the parent type is published.
We could approach it like this:
SELECT t.id AS `type`
, t.title AS `title`
, COUNT(o.id) AS `cnt`
FROM types t
LEFT
JOIN objects o
ON o.type = t.id
AND o.published = 1
AND t.published = 1
LEFT
JOIN objects op
ON op.id = o.parent
LEFT
JOIN types pt
ON pt.id = op.type
WHERE -- this not a child (there is no parent)
op.id IS NULL
OR -- parent is published and parent type is published
( op.published = 1 AND pt.published = 1 )
GROUP
BY t.id
, t.title
I have a Profile table like this
|--------|-----------|
| People | Favorite |
|--------|-----------|
| A | Movie |
| B | Movie |
| B | Jogging |
|--------|-----------|
Q: How to retrieve the people whose favorite is movie but not jogging?
In this table, the result is only People A.
Although I came out with this
select People from Profile
where
People
in
(select People from Profile
where favorite='Movie')
and
People
not in
(select People from Profile
where favorite='Jogging')
But it seem like can be better, any suggestion or answer (without using join or union clause)?
https://www.db-fiddle.com/f/rboiDpxxbABCpjtduEz7uY/1
SELECT People
FROM `profile`
GROUP BY people
HAVING SUM('Movie' = favorite) > 0
AND SUM('Jogging' = favorite) = 0
There's lots of ways. While you can use a UNION, its rather messy and innefficient. MySQL doesn't have a MINUS clause which would give a fairly easy to understand query.
You could aggregate the data:
SELECT people
, MAX(IF(favorite='jogging', 1, 0)) as jogging
, MAX(IF(favorite='movie', 1, 0)) as movie
FROM profile
GROUP BY people
HAVING movie=1 AND jogging=0
Or use an outer join:
SELECT m.people
FROM profile m
LEFT JOIN
( SELECT j.people
FROM joggers j
WHERE j.favorite='jogging' ) joggers
ON m.people=joggers.people
WHERE joggers.people IS NULL
AND m.favorite='movies'
Using a NOT IN/NOT EXISTS gives clearer syntax but again would be very innefficient.
There are several query patterns that will return a result that satisfies the specification.
We can use NOT EXISTS with a correlated subquery:
SELECT p.people
FROM profile p
WHERE p.favorite = 'Movie'
AND NOT EXISTS ( SELECT 1
FROM profile q
WHERE q.favorite = 'Jogging'
AND q.people = p.people /* related to row in out query */
)
ORDER
BY p.people
An equivalent result can also be done with an anti-join pattern:
SELECT p.people
FROM profile p
LEFT
JOIN profile q
ON q.people = p.people
AND q.favorite = 'Jogging'
WHERE q.people IS NULL
AND p.favorite = 'Movie'
ORDER BY p.people
Another option is conditional aggregation. Without a guarantee about uniqueness, and some MySQL shorthand:
SELECT p.people
FROM profile p
GROUP
BY p.people
HAVING 1 = MAX(p.favorite='Movie')
AND 0 = MAX(p.favorite='Jogging')
A more portable more ANSI standard compliant syntax for the conditional aggregation:
SELECT p.people
FROM profile p
GROUP
BY p.people
HAVING 1 = MAX(CASE p.favorite WHEN 'Movie' THEN 1 ELSE 0 END)
AND 0 = MAX(CASE p.favorite WHEN Jogging' THEN 1 ELSE 0 END)
This is a common problem when you want to have multiple conditions with the same column. I have answered this here and there are other methods like intersect and subqueries.
SELECT people, GROUP_CONCAT(favorite) as fav
FROM profile
GROUP BY people
HAVING fav REGEXP 'Movie'
AND NOT fav REGEXP 'Jogging';
With group by people and checking the minimum and maximum values of favorite to be 'Movie':
select people from tablename
where favorite in ('Movie', 'Jogging')
group by people
having min(favorite) = 'Movie' and max(favorite) = 'Movie'
Supposing I have a table where a material has asignments of different characteristics. A material can have one or more charateristics. Then I would like to find to a certain material similar materials, that means at least 2 characteristics should match. In this example I should find material C when I compare with A and D should find B. Is there any solution in SQL?
material | character
----------------------
A | 2
A | 5
B | 1
B | 3
B | 4
C | 2
C | 5
D | 3
D | 1
This is an Entity-Attribute-Value table, and it notoriously painful to search. (In this case, the value is implied as being TRUE for has this attribute.)
It involves comparing everything against everything, grouping the results, and checking if the groups match. Virtually no use of indexes or intelligence of any kind.
SELECT
material_a.material AS material_a,
material_b.material AS material_b
FROM
material AS material_a
LEFT JOIN
material AS material_b
ON material_a.character = material_b.character
AND material_a.material <> material_b.material
GROUP BY
material_a.material,
material_b.material
HAVING
0 = MAX(CASE WHEN material_b.character IS NULL THEN 1 ELSE 0 END)
This gives every material_b that has all of the characteristics that material_a has.
- The HAVING clause will check that every 0 of material a's characteristics are missing from material b.
Changing to an INNER JOIN and changing the HAVING CLAUSE will get the share at least two materials.
SELECT
material_a.material AS material_a,
material_b.material AS material_b
FROM
material AS material_a
INNER JOIN
material AS material_b
ON material_a.character = material_b.character
AND material_a.material <> material_b.material
GROUP BY
material_a.material,
material_b.material
HAVING
COUNT(*) >= 2
Either way, you still are joining the whole table against the whole table, then filtering out the failures. With 100 materials, that's 9,900 material-material comparison. Imagine when you have 1000 materials and have 999,000 comparisons. Or 1million materials...
You could use something like the following grouped table to determine all items with more than 2 similar characteristics
SELECT
material = t1.material
, similarMaterial = t2.material
FROM
tableName t1
INNER JOIN tableName t2 ON t1.character = t2.character AND NOT(t1.material = t2.material)
GROUP BY material
HAVING
COUNT(*) >= 2
Yes, you can find all paired of similar materials with SQL similar to this:
SELECT c1.material, c2.material, COUNT(*) as characterCount
FROM charateristics c1
CROSS JOIN charateristics c2
WHERE c1.material > c2.material AND c1.character = c2.character
GROUP BY c1.material, c2.material
HAVING characterCount >= 2;
This would give you the results based on a material input:
SELECT b.material
FROM table1 a
INNER JOIN table1 b
ON a.character = b.character AND a.material <> b.material
WHERE a.material = 'A' -- Your input
GROUP BY b.material
HAVING COUNT(*) > 1;
sqlfiddle demo
Or do this to give you the pairs:
SELECT a.material as LEFT_MATERIAL ,b.material AS RIGHT_MATERIAL
FROM table1 a
INNER JOIN table1 b ON a.character = b.character AND a.material <> b.material
GROUP BY a.material,b.material
HAVING COUNT(*) > 1;
sqlfiddle demo
A Company has many Reviews which has Rating Column itself.
CompID Ratig
12 3
13 3
17 4
22 4
23 5
24 3
28 3,2
This is what I need to be set to each company by id. Now Rating In Company Column is NULL.
I've written something like this:
UPDATE Companies c
JOIN Reviews r on c.CompanyID = r.CompanyID
SET c.Rating = AVG(r.rating)
group by r.CompanyID
This should do what you want using a simple nested query, in this case probably simpler than a JOIN.
UPDATE Companies
SET Rating =
(SELECT AVG(Rating)
FROM Ratings
WHERE Companies.CompanyId = Ratings.CompId)
Simple SQLfiddle demo here.
EDIT: If you really want to use a JOIN/UPDATE FROM, it'd look something like this;
UPDATE c
SET c.Rating = r.Rating
FROM Companies c
JOIN (SELECT AVG(Rating) Rating, CompID FROM Ratings GROUP BY CompId) r
ON c.CompanyId = r.CompId
At least to me, somewhat more complicated to read, and afaik it only works on SQL Server, but here's the SQLfiddle for that too :)
UPDATE ComisionesxColaboradorxLineaPrescripciones
SET CANTIDAD_PRODUCTOS_CORE_CUMPLE = CANTIDAD
FROM #ComisionesxColaboradorxLineaPrescripciones ComisionesxColaboradorxLineaPrescripciones
INNER JOIN
(SELECT TAB_L.COD_COLAB AS COD_COLAB,TAB_L.TIPO_COLABORADOR AS TIPO_COLABORADOR, COUNT(TAB_P.COD_SEG) AS CANTIDAD
FROM #ComisionesxColaboradorxLineaPrescripciones TAB_L
INNER JOIN #ComisionesxColaboradorxLineaxProductoPrescripciones TAB_P
ON TAB_L.COD_COLAB=TAB_P.COD_COLAB AND
TAB_L.TIPO_COLABORADOR=TAB_P.TIPO_COLABORADOR
GROUP BY TAB_L.COD_COLAB,TAB_L.TIPO_COLABORADOR
) AGRUPADO
ON ComisionesxColaboradorxLineaPrescripciones.COD_COLAB = AGRUPADO.COD_COLAB AND
ComisionesxColaboradorxLineaPrescripciones.TIPO_COLABORADOR = AGRUPADO.TIPO_COLABORADOR
How do I rewrite this query to show the correct value for the sub query claims if there isn't any record in listings for this account_id, but there is 1 record in claimed_listings. Thanks!
SELECT status, (SELECT count(id)
FROM claimed_listings
WHERE account_id = 1) AS claims
FROM listings
WHERE account_id = 1
I'm expecting to see a result like
status | claims
A | 1
F | 1
E | 1
in this case there are three listings and 1 claim listing.
Problem is if there is no listing(s) and 1 claim listing i get no results at all?
try this:
SELECT a.Account_ID, Count(b.Account_ID) TotalAcount
FROM claimed_Listings a LEFT JOIN listings b
on a.account_ID = b.Account_ID
WHERE a.Account_ID = 1
GROUP BY a.Account_ID
Try the following (hope I got you right..)
select count(cl.id), count(cl.account_id)
from claimed_listings cl
where cl.account_id=1 and cl.account_id not in (select account_id from listings)
group by cl.id
having count(cl.account_id) > 0;