Excluding null results and counting number of rows based on several elements - mysql

I have the tables 'template' and 'object' at a relationship one to many.
I need to know how many 'objects' share the same 'template'.
This is simple enough but there are two columns in the object table, 'theme' and 'active'.
I need to add to my query to return the count of:
How many objects share the same template.
and have different 'object.active's (active is boolean, and is never null)
(so if there are three 'object's sharing the same 'template' then the count will not be incremented)
and have different object.theme's ('theme' is varchar and can be null)
'theme' will only have a value if object.active is true
excluding null object.themes
My biggest problem is that if 'object.active' all have the same value '1' or '0' then it should not add to the count, but if they all have the same value '1' and each have different object.theme's then they do need to add to the count.
So far I am at the following but when going through manually and counting what the figure should be I get an incorrect result:
SELECT sum(tmpUse) FROM(
SELECT COUNT(*) AS tmpUse,tmp.title FROM templates tmp
LEFT JOIN assessmentinstances ai ON ai.template_id = tmp.id
GROUP BY tmp.id
HAVING tmpUse>1
AND COUNT(DISTINCT ai.data_theme)>1
AND COUNT(DISTINCT ai.mobile_ready)>1
) alias
template table
_____
| id |
-----
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
-----
object table
____________________________________
| id | template_id | active | theme |
|-----|-------------|--------|--------|
| a | 1 | 0 | null |
| b | 1 | 1 | x |
| c | 1 | 1 | y |
| d | 3 | 1 | x |
| e | 3 | 0 | null |
| f | 1 | 1 | z |
| g | 2 | 1 | z |
| h | 2 | 0 | null |
| i | 4 | 1 | y |
| j | 5 | 1 | z |
| k | 1 | 1 | x |
| l | 1 | 0 | null |
| m | 1 | 0 | null |
| n | 3 | 0 | null |
| o | 3 | 1 | x |
|-------------------------------------|
The result I would hope from these tables would be:
id count
1 3
2 1
3 1
4 0
5 0
= 5
Template id 1 has 7 objects, objects include both 0 and 1 actives so look at the themes. the themes associated are the followinf: null, x, y, z, x, null, null. We ignore nulls and duplicates so this would add 3 to the count.
Template id 2 has 2 objects, one is active 1 and one 0, because these are different but only one distinct theme we can add 1 to the count.
Template id 3 has 2 active 1's and two active 0's so we know at least one will be added to the count. looking at their themes, they are the same so no more to the count, so only 1 is added for template id 3.
Template id 4 and 5 both have one object so we know this will not add to the count.
so the output from the query will be:
'5'

try this.
select sum(themecount) from (select template.templateid,
count(distinct case when object.theme IS NOT NULL then object.theme end) themeCount
from Object object where
count(distinct object.isActive)>1
group by object.template_id)

Related

How to include o values results in COUNT? SQL Query

I need to count shutouts for goalies. I have two tables ("players" and "gamestats"). I have a problem getting the values when goalie has no "0" / zero values in "goalsagainst" column when the goalie is "dressed" (has games).
So, I need to count all the zero values from column "goalsagainst" when "dressed" column has a value 1 as a "shutout" column. And if values from column "goalsagains" are more than 0 "shutout" colmn values should be 0;
I have tried the other solutions from similar topics, but I always have the same outcome where only the zero values are counted and other values are not shown.
My structure:
players
|p_id|pos|
--------
| 1 | G |
--------
| 2 | D |
--------
| 3 | O |
--------
| 4 | G |
stats
|g_id|p_id|goalsagainst|dressed|
--------------------------------
| 1 | 1 | 2 | 1 |
--------------------------------
| 1 | 4 | 0 | 0 |
--------------------------------
| 1 | 3 | NULL | 1 |
--------------------------------
| 1 | 2 | NULL | 1 |
--------------------------------
| 2 | 1 | 0 | 0 |
--------------------------------
| 2 | 4 | 0 | 1 |
--------------------------------
| 2 | 3 | NULL | 1 |
--------------------------------
| 2 | 2 | NULL | 1 |
SELECT
stats.id,
COUNT(stats.goalsagainst) AS shutouts
FROM `stats`
RIGHT JOIN players
ON stats.id = players.id
WHERE goalsagainst = 0
AND players.pos = 'G'
AND stats.dressed = 1
GROUP BY stats.id;
my result is:
p_id|shutouts
-------------
4 | 1
when it should be:
p_id|shutouts
-------------
1 | 0
-------------
4 | 1
Your problem is that your conditions on the stats table in your WHERE clause effectively turn the RIGHT JOIN into an INNER JOIN. To work around this, move the conditions to the ON clause. Secondly, you need to use players.p_id in the SELECT and GROUP BY as stats.p_id may be NULL:
SELECT players.p_id,
COUNT(stats.goalsagainst) AS shutouts
FROM `stats`
RIGHT JOIN players ON stats.p_id = players.p_id AND stats.dressed = 1 AND stats.goalsagainst = 0
WHERE players.pos = 'G'
GROUP BY players.p_id;
Output:
p_id shutouts
1 0
4 1
Demo on dbfiddle

Automatically inserting additional columns in MySQL 8.0 [duplicate]

This question already has an answer here:
MySQL pivot row into dynamic number of columns
(1 answer)
Closed 3 years ago.
Say I have a table like so
+----+----------+------+
| id | name | type |
+----+----------+------+
| 1 | apple | F |
| 1 | pear | F |
| 1 | cucumber | V |
| 2 | orange | F |
| 2 | grass | NULL |
| 2 | broccoli | V |
| 3 | flower | NULL |
| 3 | tomato | NULL |
+----+----------+------+
I want to end up with a table that counts the number of elements for each type (including NULL types) AND for each id, like this:
+----+-----------------+--------------+--------------+
| id | type_NULL_count | type_F_count | type_V_count |
+----+-----------------+--------------+--------------+
| 1 | 0 | 2 | 1 |
| 2 | 1 | 1 | 1 |
| 3 | 2 | 0 | 0 |
+----+-----------------+--------------+--------------+
This is rather easy to do, but is there a way (a query I can write or something else) such that when I go back and edit one of the type fields in the first table, I end up with a properly updated count table?
For example, let's say I want to add a new type (type X) and change the type field for flower from NULL to X. Is there a way to end up with the following table without having to rewrite the query or add more statements?
+----+-----------------+--------------+--------------+--------------+
| id | type_NULL_count | type_F_count | type_V_count | type_X_count |
+----+-----------------+--------------+--------------+--------------+
| 1 | 0 | 2 | 1 | 0 |
| 2 | 1 | 1 | 1 | 0 |
| 3 | 1 | 0 | 0 | 1 |
+----+-----------------+--------------+--------------+--------------+
I'm not sure if this is the best way to do this, so I am open to suggestions
Having a secondary table which it's number of columns changes based on your first table is not a viable option.
Do you need to keep the result in a table or it will be displayed as a report?
I think a better way to do this is using the SQL below calculate counts by id plus type and display using your data display tool the way you like it.
select id, type, count(*) count
from d
group by 1,2
order by 1,2
The output would be
id type count
1 F 2
1 V 1
2 F 1
2 V 1
2 1
3 X 1
3 1

MySQL Query - Update field with order/count but start at 1 again based on another field

I have a table of products. This table was created with a SELECT from X ORDER by Y query. I want to add sequential row count or order (1,2,3..).
However, I want this count to reset to 1 when the product category or vendor changes. (I'll end up with a order to sort by when querying a combination of product category and vendor).
This problem is simplification of a sub-problem related to a larger issue. So, other solutions involving php aren't relevant.
Here's a sample table:
+--------------+------------------+-----------+-----------+
| product_name | product_category | vendor_id | sortorder |
+--------------+------------------+-----------+-----------+
| Product 1 | A | 1 | 0 |
| Product 2 | A | 1 | 0 |
| Product 3 | A | 1 | 0 |
| Product 4 | B | 1 | 0 |
| Product 5 | B | 1 | 0 |
| Product 6 | C | 2 | 0 |
| Product 7 | C | 2 | 0 |
| Product 8 | C | 2 | 0 |
| Product 9 | C | 2 | 0 |
| Product 10 | C | 2 | 0 |
+--------------+------------------+-----------+-----------+
This is how it should look if the query is run successfully:
+--------------+------------------+-----------+-----------+
| product_name | product_category | vendor_id | sortorder |
+--------------+------------------+-----------+-----------+
| Product 1 | A | 1 | 1 |
| Product 2 | A | 1 | 2 |
| Product 3 | A | 1 | 3 |
| Product 4 | B | 1 | 1 |
| Product 5 | B | 1 | 2 |
| Product 6 | C | 2 | 1 |
| Product 7 | C | 2 | 2 |
| Product 8 | C | 2 | 3 |
| Product 9 | C | 2 | 1 |
| Product 10 | C | 2 | 1 |
+--------------+------------------+-----------+-----------+
I have tried a TON of different queries related to this answer, mostly to try and get this result from the initial query, but to no avail:
Using LIMIT within GROUP BY to get N results per group?
I could run a query like this to get it ordered 1,2,3,10):
SET #pos = 0;
UPDATE testtable SET sortorder = ( SELECT #pos := #pos + 1 );
But, that doesn't accomplish what I want, which is the count to start over again at 1 when the 'product_category' changes between Product 3 and Product 4.
In bad syntax, this is what I want to do:
SET #pos = 0;
UPDATE testtable SET sortorder =
// { if (product_category != [last product_category]
// OR
// if (vendor_id != [last vendor_id])
// }
// THEN SET sortorder = 1
// ELSE SET sortorder = (1+ [last sortorder]
;
Thanks as always...
EDIT-9.12.2016
Trying the solution from #Fancypants. Actually, at first it appears not to work, but it has to do with the "product_name" field sort order. It puts Product 10 before product 5 (1 comes before 5). Once I account for that by using an integer field instead, the result is perfect.
I assume you have an error in your desired result. Sortorder for Product 9 and 10 should be 4 and 5, right?
Here's how you can do it:
UPDATE t
JOIN (
SELECT
t.*
, #rc := IF(#prevpc != product_category OR #prevv != vendor_id, 1, #rc + 1) AS so
, #prevpc := product_category
, #prevv := vendor_id
FROM
t
, (SELECT #prevpc := NULL, #prevv := NULL, #rc := 0) var_init_subquery
ORDER BY product_category, vendor_id, product_name
) sq ON t.product_name = sq.product_name
SET t.sortorder = sq.so;
see it working live in an sqlfiddle

Loop through each record of a table and preform calculations on all other records

I want to calculate a value for NO_TOP_RATING in my table working
The calculation for NO_TOP_RATING is made by:
For each row, get all other rows that fall within the previous year from ANNDATS_CONVERTED for that record, and have the same ESTIMID as that record.
From those, find the lowest IRECCD value.
Then, count the number of times that the same ANALYST has an IRECCD that matches the lowest IRECCD calculated.
NOTE: This should omit the current row being calculated (so to find the value for row id 1, do not use this row in the calculations) and any records where ANALYST is blank should be ignored altogether.
TABLE working:
| ID | ANALYST | ESTIMID | ANNDATS_CONVERTED | IRECCD | NO_TOP_RATING |
---------------------------------------------------------------------------------
| 1 | DAVE | Brokerage000 | 1998-07-01 | 2 | |
| 2 | DAVE | Brokerage000 | 1998-06-28 | 2 | |
| 3 | DAVE | Brokerage000 | 1998-07-02 | 4 | |
| 4 | DAVE | Brokerage000 | 1998-07-04 | 3 | |
| 5 | SAM | Brokerage000 | 1998-06-14 | 1 | |
| 6 | SAM | Brokerage000 | 1998-06-28 | 4 | |
| 7 | | Brokerage000 | 1998-06-28 | 1 | |
| 8 | DAVE | Brokerage111 | 1998-06-28 | 5 | |
So - when calculating NO_TOP_RATING for record #1:
record #1 would not be included in the calculation, because I want to omit it from the calculation
record #7 would not be included in the calculation at all, because ANALYST is blank
Record #8 would not be included in the calculation, because ESTIMID is not the same as record #1
EXPECTED RESULT:
TABLE working:
| ID | ANALYST | ESTIMID | ANNDATS_CONVERTED | IRECCD | NO_TOP_RATING |
---------------------------------------------------------------------------------
| 1 | DAVE | Brokerage000 | 1998-07-01 | 2 | 0 |
| 2 | DAVE | Brokerage000 | 1998-06-28 | 2 | 0 |
| 3 | DAVE | Brokerage000 | 1998-07-02 | 4 | 0 |
| 4 | DAVE | Brokerage000 | 1998-07-04 | 3 | 0 |
| 5 | SAM | Brokerage000 | 1998-06-14 | 1 | 0 |
| 6 | SAM | Brokerage000 | 1998-06-28 | 4 | 1 |
| 7 | | Brokerage000 | 1998-06-28 | 1 | |
| 8 | DAVE | Brokerage111 | 1998-06-28 | 5 | 0 |
Here is the MySQL I have so far:
UPDATE `working`
SET `working`.`NO_TOP_RATING` =
(
SELECT COUNT(`ID`) FROM (SELECT `ID`,`IRECCD`,`ESTIMID` FROM `working`) AS BB
WHERE
`IRECCD` =
(
SELECT COUNT(`ID`) FROM (SELECT `ID`,`IRECCD`,`ESTIMID`, `ANALYST` FROM `working`) AS ZZ
WHERE
`IRECCD` =
-- this calculates the LOWEST number with same `ESTIMID`
(
SELECT MIN(`IRECCD`)
FROM (SELECT `ID`,`IRECCD`,`ANNDATS_CONVERTED`,`ESTIMID` FROM `working`) AS CC
WHERE
`ANNDATS_CONVERTED` >= DATE_SUB(`ANNDATS_CONVERTED`,INTERVAL 1 YEAR)
AND
`working`.`ESTIMID` = BB.`ESTIMID`
)
-- END this calculates the LOWEST number with same `ESTIMID`
AND
`working`.`ANALYST` = ZZ.`ANALYST`
)
)
WHERE `working`.`ANALYST` != ''
This is working in PHP, looping through each record and evaluating all the other records for each. This involves looping and takes a very long time on a large database. I am trying to achieve the same result with MySQL.
I took a few steps to solve this. The first thing I did was write a JOIN that got all of the rows I needed. I joined the table to itself on several conditions:
The estimid matched
The id value was not the same
The analyst column was not null in either table
The anndats_converted of one table was within the previous year of the other table.
To test, I selected the id from both tables to make sure I was getting proper pairings:
SELECT w.id, wo.id
FROM working w
JOIN working wo
ON w.estimid = wo.estimid
AND w.id != wo.id
AND w.analyst IS NOT NULL
AND wo.analyst IS NOT NULL
AND wo.anndats_converted BETWEEN DATE_SUB(w.anndats_converted, INTERVAL 1 YEAR) AND w.anndats_converted
ORDER BY w.id;
A brief result set showed the following pairings:
| id | id |
+----+----+
| 1 | 2 |
| 1 | 5 |
| 1 | 6 |
| 2 | 5 |
| 2 | 6 |
This seems to match what you wanted. For id #1, row 1 is excluded (because it is being calculated) rows 3 and 4 do not fall in the proper date range, row 7 is null and row 8 is a different estimid.
Then, I used an aggregate function to calculate the minimum ireccd by grouping by the first table:
SELECT w.id, w.analyst, MIN(wo.ireccd) AS min_ireccd
FROM working w
JOIN working wo
ON w.estimid = wo.estimid
AND w.id != wo.id
AND w.analyst IS NOT NULL
AND wo.analyst IS NOT NULL
AND wo.anndats_converted BETWEEN DATE_SUB(w.anndats_converted, INTERVAL 1 YEAR) AND w.anndats_converted
GROUP BY w.id;
The next part was also tricky so I'll explain it in two steps. I joined the above query with the original table, with the only condition that the analyst column matched. What this did was create a Cartesian Product, in a way. The query looked like this:
SELECT *
FROM working w
LEFT JOIN(
SELECT w.id, w.analyst, MIN(wo.ireccd) AS min_ireccd
FROM working w
LEFT JOIN working wo
ON w.estimid = wo.estimid
AND w.id != wo.id
AND w.analyst IS NOT NULL
AND wo.analyst IS NOT NULL
AND wo.anndats_converted BETWEEN DATE_SUB(w.anndats_converted, INTERVAL 1 YEAR) AND w.anndats_converted
GROUP BY w.id) temp ON temp.analyst = w.analyst;
And I saw all possible pairings for each person, like this:
| id | analyst | ireccd | id | analyst | min_ireccd |
+----+---------+--------+----+---------+------------+
| 1 | DAVE | 2 | 8 | DAVE | null |
| 1 | DAVE | 2 | 4 | DAVE | 1 |
| 1 | DAVE | 2 | 1 | DAVE | 1 |
| 1 | DAVE | 2 | 2 | DAVE | 1 |
| 1 | DAVE | 2 | 3 | DAVE | 1 |
Notice that compares the first DAVE with all other rows of DAVE in the table. ALSO NOTE I changed the above inner query to include an outer join so that all rows were considered. If there was nothing to calculate, the min_ireccd would be null.
The last thing I did was use that result set, and count the number of times the ireccd matched the min_ireccd. I grouped by id, so in the above sample set, it never matches, so the count would be 0. Here is the final query. It leaves null values (row 7) as null because that's what your expected results show:
SELECT w.*, SUM(w.ireccd = temp.min_ireccd) AS NO_TOP_RATING
FROM working w
LEFT JOIN(
SELECT w.id, w.analyst, MIN(wo.ireccd) AS min_ireccd
FROM working w
LEFT JOIN working wo
ON w.estimid = wo.estimid
AND w.id != wo.id
AND w.analyst IS NOT NULL
AND wo.analyst IS NOT NULL
AND wo.anndats_converted BETWEEN DATE_SUB(w.anndats_converted, INTERVAL 1 YEAR) AND w.anndats_converted
GROUP BY w.id) temp ON temp.analyst = w.analyst
GROUP BY w.id;
These are the results I got:

mysql getting data and looking it up in another table

I've got two tables in my database. Table 1 is a list of "timelines" and their corresponding owners and title.
Table 2 is a list of users who have access to the timelines but are followers, not owners.
I'm trying to write a query that outputs the lineID's and corresponding titles that are linked to a userID in either of the two tables.
A query for userID 1 would ideally output:
1 a
2 b
3 c
6 f
Hopefully this isn't too confusing but the purpose is to fill a dynamically generated select box with the LineID and Title for a given UserID...
Table 1 ("owners")
--------------------------
| LineID | UserID | Title |
| 1 | 1 | a |
| 2 | 1 | b |
| 3 | 1 | c |
| 4 | 2 | d |
| 5 | 2 | e |
| 6 | 1 | f |
--------------------------
Table 2 ("followers")
----------------------------
| RowID | LineID | UserID |
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 3 | 1 |
| 4 | 3 | 2 |
| 5 | 2 | 2 |
| 6 | 6 | 1 |
----------------------------
I tried using:
SELECT title
FROM `lines`
LEFT JOIN follow
ON follow.user_id = lines.user_id
WHERE follow.user_id = 1
That ended up producing duplicate rows.
The output I need would ideally be an array consisting of all the lineID's and Titles associated with that userID.
select LineId, Title
from owners
where LineId in (select LineId from followers group by LineId )
order by owners.LineId