How can I treat with NULL as minimum value? - mysql

I have a table like this:
// notifications
+----+-----------+-------+---------+---------+------+
| id | score | type | post_id | user_id | seen |
+----+-----------+-------+---------+---------+------+
| 1 | 15 | 1 | 2342 | 342 | 1 |
| 2 | 5 | 1 | 2342 | 342 | 1 |
| 3 | NULL | 2 | 5342 | 342 | 1 |
| 4 | -10 | 1 | 2342 | 342 | NULL |
| 5 | 5 | 1 | 2342 | 342 | NULL |
| 6 | NULL | 2 | 8342 | 342 | NULL |
| 7 | -2 | 1 | 2342 | 342 | NULL |
+----+-----------+-------+---------+---------+------+
-- type: 1 means "it is a vote", 2 means "it is a comment (without score)"
Here is my query:
SELECT SUM(score), type, post_id, seen
FROM notifications
WHERE user_id = 342
GROUP BY type, post_id
ORDER BY (seen IS NULL) desc
As you see, there is SUM() function, Also both type and post_id columns are in the GROUP BY statement. Well now I'm talking about seen column. I don't want to put it into GROUP BY statement. So I have to use either MAX() or MIN() for it. Right?
Actually I need to select NULL as seen column (in query above) if there is even one row which has seen = NULL. My current query selects 1 as seen's value, even when I use MIN(seen). So why 1 is minimum when there is NULL?
Also I want to order rows so that all SEEN = NULL be in the top of list. How can I do that?
Expected result:
// notifications
+-----------+-------+---------+------+
| score | type | post_id | seen |
+-----------+-------+---------+------+
| 13 | 1 | 2342 | NULL |
| NULL | 2 | 8342 | NULL |
| NULL | 2 | 5342 | 1 |
+-----------+-------+---------+------+

You could do this
case when sum(seen is null) > 0
then null
else min(seen)
end

You could use the following query:
SELECT SUM(score), type, post_id, min(IFNULL(seen, 0)) as seen
FROM notifications
WHERE user_id = 342
GROUP BY type, post_id
ORDER BY seen desc

Related

Count null from joined table in MySQL

I need a count of NULL from 2 tables that are joined in MySQL. Sample data like this:
datefield FROM TABLE calendar (contain dates from start to end of this year)
-----------
TABLE value (data stored)
+------------+-------+
| date | keter |
+------------+-------+
| 2021-08-01 | 11 |
| 2021-08-04 | 0 |
| 2021-08-07 | 20 |
| 2021-08-08 | 15 |
| 2021-08-11 | 0 |
+------------+-------+
I am using the following query to combine and display data from calendar and value tables.
SELECT datefield,keter FROM calendar
LEFT JOIN kehadiran ON datefield=tgl AND id_kar IN ('110101')
WHERE datefield BETWEEN '2021-08-01' AND '2021-08-15' GROUP BY datefield;
result :
+------------+-------+
| datefield | keter |
+------------+-------+
| 2021-08-01 | 11 |
| 2021-08-02 | NULL |
| 2021-08-03 | NULL |
| 2021-08-04 | 0 |
| 2021-08-05 | NULL |
| 2021-08-06 | NULL |
| 2021-08-07 | 20 |
| 2021-08-08 | 15 |
| 2021-08-09 | NULL |
| 2021-08-10 | NULL |
| 2021-08-11 | 0 |
| 2021-08-12 | NULL |
| 2021-08-13 | NULL |
| 2021-08-14 | NULL |
| 2021-08-15 | NULL |
+------------+-------+
I use query based on this question (3 table join counting nulls), I didn't get the result I wanted. The query is this :
SELECT SUM(k.keter) FROM kehadiran k
LEFT OUTER JOIN calendar c ON c.datefield = k.keter AND id_kar IN ('110101')
WHERE datefield BETWEEN '2021-08-01' AND '2021-08-12' AND k.keter is NULL;
result:
+--------------+
| SUM(k.keter) |
+--------------+
| NULL |
+--------------+
the result i wanted :
+--------------+
| SUM(k.keter) |
+--------------+
| 10 |
+--------------+
How should I count NULL from the joined table as mentioned above?
You swapped the tables in your last query which is incorrect. Use the query that worked and use COUNT(*) with WHERE right_table.any_notnull_column IS NULL:
SELECT COUNT(*)
FROM calendar
LEFT JOIN kehadiran k ON datefield=tgl AND id_kar IN ('110101')
WHERE datefield BETWEEN '2021-08-01' AND '2021-08-15'
AND k.keter is NULL
You sum up NULL in your query. And a Sum of NULL is NULL. You should just replace SUM(k.keter) with COUNT(k.keter)
See for a small example
To count NULLs, you can use:
SUM(k.keter IS NULL)
Or:
COUNT(*) - COUNT(k.keter)

MySQL GROUP BY column ignorning specific value

I have a query on a table of phone calls that uses GROUP BY to show only unique caller ids. The problem is that if a caller has caller id blocking their caller id shows up as "Unknown" and the client doesn't want all Unknowns to be summed up together. So basically, instead of just GROUP BY caller_id I need to somehow do something like GROUP BY caller_id IF caller_id != 'Unknown'
Is this even possible? I'd like to avoid doing all the group processing in PHP if at all possible.
You can do something like:
SELECT caller_id FROM phone_calls WHERE caller_id != 'Unknown' GROUP BY caller_id;
or consider DISTINCT - in most cases it is faster - if you have index created on caller_id the performance is usually the same, but if not DISTINCT is better. If you need :
... show only unique caller ids ..
but maybe for your case (doing aggregation or something similar) you are not able to use it but just in case:
SELECT DISTINCT caller_id FROM phone_calls WHERE caller_id != 'Unknown';
-- EDIT AFTER discussion in comments
SELECT * FROM callers;
+----+-----------+-----------+
| id | caller_id | call_time |
+----+-----------+-----------+
| 1 | abc | 24 |
| 2 | abc | 16 |
| 3 | xyz | 10 |
| 4 | xyz | 10 |
| 5 | Unknown | 11 |
| 6 | Unknown | 12 |
| 7 | Unknown | 13 |
| 8 | xyz | 1 |
| 9 | abc | 10 |
+----+-----------+-----------+
SELECT caller_id, SUM(call_time) FROM callers
WHERE caller_id != 'Unknown'
GROUP BY caller_id;
+-----------+----------------+
| caller_id | SUM(call_time) |
+-----------+----------------+
| abc | 50 |
| xyz | 21 |
+-----------+----------------+
SELECT caller_id, SUM(call_time) FROM callers
GROUP BY caller_id;
+-----------+----------------+
| caller_id | SUM(call_time) |
+-----------+----------------+
| abc | 50 |
| Unknown | 36 |
| xyz | 21 |
+-----------+----------------+
SELECT caller_id, SUM(call_time) as total_time FROM callers
WHERE caller_id != 'Unknown'
GROUP BY caller_id
UNION
SELECT caller_id, call_time FROM callers
WHERE caller_id = 'Unknown';
+-----------+------------+
| caller_id | total_time |
+-----------+------------+
| abc | 50 |
| xyz | 21 |
| Unknown | 11 |
| Unknown | 12 |
| Unknown | 13 |
+-----------+------------+
SELECT caller_id, SUM(call_time) as total_time FROM callers
GROUP BY caller_id,
(case when caller_id = 'Unknown'
AND id is not null
then id end
);
+-----------+------------+
| caller_id | total_time |
+-----------+------------+
| abc | 50 |
| Unknown | 11 |
| Unknown | 12 |
| Unknown | 13 |
| xyz | 21 |
+-----------+------------+

Counting multiple value ocurrences on multiple columns in a single query

I have a search section for looking up products which has a navigation bar for filtering purposes that shows the total results of each product feature. For example:
TOTAL RESULTS 60
New (32)
Used (28)
Particular (10)
Company (50)
In mysql I have the following queries (one per feature)
Type
SELECT a.id_type, whois.name as whoisName, COUNT(a.id_type) as countWhois
FROM (published a
INNER JOIN types whois ON whois.id = a.id_type)
GROUP BY id_type
+---------+------------+------------+
| id_type | whoisName | countWhois |
+---------+------------+------------+
| 0 | Company | 50 |
| 1 | Particular | 10 |
+---------+------------+------------+
Condition
SELECT a.id_condition, cond.name as condName, COUNT(a.id_condition) as countCondition
FROM (published a
INNER JOIN conditions cond ON cond.id = a.id_condition)
GROUP BY id_condition
+--------------+---------------+----------------+
| id_condition | conditionName | countCondition |
+--------------+---------------+----------------+
| 0 | New | 32 |
| 1 | Used | 28 |
+--------------+---------------+----------------+
I want to summarize the two queries in a single one but canĀ“t figure out how. I was thinking something like this:
+---------+------------+------------+--------------+---------------+----------------+
| id_type | whoisName | countWhois | id_condition | conditionName | countCondition |
+---------+------------+------------+--------------+---------------+----------------+
| 0 | Company | 50 | NULL | NULL | NULL |
| 1 | Particular | 10 | NULL | NULL | NULL |
| NULL | NULL | NULL | 0 | New | 32 |
| NULL | NULL | NULL | 1 | Used | 28 |
+---------+------------+------------+--------------+---------------+----------------+
Is this possible?
Thanks and sorry if my English is bad, it's not my native language.

Update innerquery result

I have a query and a result as follows.
In the database NULL and 0 represent the same meaning.
Now I want a counter based on Null+0 or 1
Eg:in the following example I want the result like this:
IsVirtual Category counter
NULL+0 3 343+8 = (351 is Total)
Query
select * from
(
Select IsVirtual, Category, count(*) as counter
from [Hardware]
group by IsVirtual, Category
) innercat
Output
+-----------+----------+---------+
| IsVirtual | Category | counter |
+-----------+----------+---------+
| NULL | 3 | 343 |
| 0 | 3 | 8 |
| 1 | 2 | 1 |
| 0 | 1 | 1 |
| NULL | 6 | 119 |
| 0 | 4 | 1 |
| NULL | 1 | 70 |
| 0 | 5 | 9 |
| NULL | 4 | 54 |
| 0 | 2 | 2 |
| NULL | 5 | 41 |
| NULL | 2 | 112 |
| 1 | 1 | 5 |
+-----------+----------+---------+
I think you want this :
SELECT COALESCE(IsVirtual, 0) as [IsVirtual],
Category,
Count(*) as [Counter]
FROM yourtable
GROUP BY COALESCE(IsVirtual, 0),Category
This will give you expected result without using subquery.
try with this
select * from (
Select CASE ISNULL(IsVirtual,0)
WHEN 0 Then 'NULL + 0'
ELSE IsVirtual
END AS IsVirtual, Category, count(*) as counter from [Hardware] group by ISNULL(IsVirtual,0), Category
)innercat
You can also do the same thing by using MAX function
This might help you.
SELECT
max(IsVirtual) as IsVirtual,
Category,
Count(*) as Counter
FROM
yourtable
GROUP BY
Category

SQL reduce number of columns in inner query

I have a query:
select
count(*), paymentOptionId
from
payments
where
id in (select min(reportDate), id
from payments
where userId in (select distinct userId
from payments
where paymentOptionId in (46,47,48,49,50,51,52,53,54,55,56))
group by userId)
group by
paymentOptionId;
The problem place is "select min(reportDate), id", this query must return 1 column result, but I can't realize how to do it while I need to group min.
The data set looks like
+----+--------+--------+-----------+---------------------+--------+----------+-----------------+
| id | userId | amount | userLevel | reportDate | buffId | bankQuot | paymentOptionId |
+----+--------+--------+-----------+---------------------+--------+----------+-----------------+
| 9 | 12012 | 5 | 5 | 2014-02-10 23:07:57 | NULL | NULL | 2 |
| 10 | 12191 | 5 | 6 | 2014-02-10 23:52:12 | NULL | NULL | 2 |
| 11 | 12295 | 5 | 6 | 2014-02-11 00:12:04 | NULL | NULL | 2 |
| 12 | 12295 | 5 | 6 | 2014-02-11 00:12:42 | NULL | NULL | 2 |
| 13 | 12256 | 5 | 6 | 2014-02-11 00:26:25 | NULL | NULL | 2 |
| 14 | 12256 | 5 | 6 | 2014-02-11 00:26:35 | NULL | NULL | 2 |
| 16 | 12510 | 5 | 5 | 2014-02-11 00:42:58 | NULL | NULL | 2 |
| 17 | 12510 | 5 | 5 | 2014-02-11 00:43:08 | NULL | NULL | 2 |
| 18 | 12510 | 18 | 5 | 2014-02-11 00:45:16 | NULL | NULL | 3 |
| 19 | 12510 | 5 | 6 | 2014-02-11 01:00:10 | NULL | NULL | 2 |
+----+--------+--------+-----------+---------------------+--------+----------+-----------------+
select count(*), paymentOptionId
from
(select userId, min(reportdate), paymentOptionId
from payments as t1
group by userId, paymentOptionId) as t2
group by paymentOptionId
Fiddle
It first gets the minimum report date (so the first entry) for every user, for every type (so there are two records for a user who has 2 types) and then counts them grouping by type (aka paymentOptionId).
By the way, you can of course cut the attributes chosen in select in from clause, they are only there so you can copy-paste it and see the results it is giving step by step.
You seem to want to report on various payment options and their counts for the earliest ReportDate for each user.
If so, here is an alternative approach
select p.paymentOptionId, count(*)
from payments p
where paymentOptionId in (46,47,48,49,50,51,52,53,54,55,56) and
not exists (select 1
from payments p2
where p2.userId = p.userId and
p2.ReportDate < p.ReportDate
)
group by paymentOptionId;
This isn't exactly the same as your query, because this will only report on the list of payment types, whereas you might want the first payment type for anyone who has ever had one of these types. I'm not sure which you want, though.