Query database to obtain first unique value - mysql

I have a pretty simple table like this:
id | custom_id | answer
----------------------------------
1 | 123 | Answer 1
__________________________________
2 | 123 | Answer 2
__________________________________
3 | 422 | Answer 3
__________________________________
4 | 345 | Answer 2
__________________________________
5 | 992 | Answer 1
__________________________________
6 | 452 | No answer
__________________________________
What I am trying to do is count the number of Answer 1, Answer 2, etc. So, for the above data I would expect to get:
2 * Answer 1
2 * Answer 2
1 * Answer 3
Note, that anything that is No answer should be discarded.
However, further to the above, I want to take into account only one answer per custom_id, and this should be their first answer. So really, the output I expect to get for the above data should be:
2 * Answer 1
1 * Answer 2
1 * Answer 3
This is because we take only the first answer for custom_id = 123.
So far, I have made the following query:
select
answer,
count(*) as totalCount
from
" . DB_TABLE . "
where
answer <> 'No answer'
group by
custom_id
However, this seems to return the total counts (as I explained first), not taking into consideration that there should only be one per custom_id. I thought the group by would solve this issue, but this does not seem to be the case.
How can I achieve the results I am after?
Thanks

One approach, will be first to create a derived table with the IDs of the first answers for every custom_id and also filter those with values No answer (since you want to ignore they), like this:
SELECT
custom_id,
MIN(id) AS firstAnswerID
FROM
<table_name>
WHERE
answer <> "No Answer"
GROUP BY
custom_id
Then, we can join the original table with this previous one on the ID column (this will act like a filter for those that aren't first answers or have the No answer value), make a GROUP BY the answer column and count the numbers of each one. In summary, this will do what you want:
SELECT
t1.answer,
COUNT(*) AS NumTimes
FROM
<table_name> AS t1
INNER JOIN
( SELECT
custom_id,
MIN(id) AS firstAnswerID
FROM
<table_name>
WHERE
answer <> "No Answer"
GROUP BY
custom_id ) AS t2 ON t2.firstAnswerID = t1.id
GROUP BY
t1.answer
ORDER BY
NumTimes DESC
You can play with this here: DB Fiddle

Try to use this:
select answer, count(answer) as totalCount from " . DB_TABLE .
" where answer <> 'No answer' group by answer
You should count rows for every answer group, not for the entire table.

Related

How Do I convert this table from row to column?

I have a table that contains id and country name, and I need to convert them so the id with more than 1 country will display in 1 row.I have been searching in this forum for over an hour and found nothing.
I tried if using the pivot function can help me to achieve the result i wanted, but I feel like using pivot does not work on my case here.
This is a mini version of the table I have. The number of distinct value in the field "country" will be over 100, so I can just say something like when county = '..' as this will be to repetitive.
enter code here
+----+--------+
| id | country|
+----+--------+
| 1 | US |
| 1 | UK |
| 2 | JP |
+----+--------+
Desired outcome I am looking for:
enter code here
+----+-----------+-----------+
| id | country_1 | country_2 |
+----+-----------+-----------+
| 1 | US | UK |
| 2 | JP | null |
+----+-----------+-----------+
I found this question which is similar but it is the opposite of what I am trying to achieve.
MySQL statement to pivot table without using pivot function or a union
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
update:
Thank you so much for all of your helps. I may not have used the queries of yours to solve my problem - as of the fact that the syntax is a bit diff running in snowflake. However, I got the insights I need from all of you.
here is my solution:
enter code here
select t1.id,
max(iff(t1.row_number = 1, t1.country ,null)) as country_1,
max(iff(t1.row_number = 2, t1.country ,null)) as country_2,
max(iff(t1.row_number = 3, t1.country, null)) as country_3
from
(
select id, country, row_number() over (partition by id order by id ) as
row_number
from table
) t1
group by t1.id
Whereas you could do it with "pivoting", what will happen when you have 3 countries? Or 4? Or 17?
May I suggest this:
SELECT id,
GROUP_CONCAT(country)
FROM tbl
GROUP BY id;
You will get something like:
1 US,UK
2 JP
use aggregation
select id, max(case when id=1 then country end ) as country_1,
max(case when id=2 then country end ) as country_2
from tbale group by id
As you comment on #Rick answer you have max 3 country for each id then you can use this
select
id,
(select country from test where test.id=t.id limit 0,1)as country_1,
(select country from test where test.id=t.id limit 1,1)as country_2,
(select country from test where test.id=t.id limit 2,1)as country_3
from test as t
group by id;
DEMO
You can try this following script with RowNumber generated per id. As you confirmed there are maximum 3 country per id, we can easily generate your desired result set by handling RowNumber 1,2 & 3
SELECT ID,
MAX(CASE WHEN RowNumber = 1 THEN country ELSE NULL END) Country_1,
MAX(CASE WHEN RowNumber = 2 THEN country ELSE NULL END) Country_2,
MAX(CASE WHEN RowNumber = 3 THEN country ELSE NULL END) Country_3
FROM
(
SELECT id,
country,
#row_num :=IF(#prev_value = concat_ws('',id),#row_num+1,1)AS RowNumber
,#prev_value := concat_ws('',id)
FROM tbale
ORDER BY id
)A
GROUP BY id
There's no "dynamic" PIVOT in SQL. You need to specify the list of columns when writing the query. Your options are:
If you know the number of columns in advance, then #ZaynulAbadinTuhin solution is the easier. It seems, however, this is not your case.
If you don't know the number of columns in advance and you want them all concatenated in a single column, then #Rick James solution is the best.
Otherwise, you can still use some kind of dynamic SQL in your app or in a stored procedure that will build the SQL query at runtime, based on the existing values of the table. But this solution would require much more programming. It's not a single/simple SQL query anymore. See Rick James's Pivoting in MySQL stored procedure.

MySQL distinct count

abc table -
ID
---
1
2
3
xyz table -
abc_id | flag
--------------
1 | 0
1 | 1
2 | 0
3 | 0
3 | 0
I need the count of distinct abc_id where if flag = 1 for a particular abc_id, then that id shouldn't be counted.
In the above example, the count should be 2. Is there any way I can achieve that? Sorry if the question has been answered before or if it's something obvious. Thanks in advance ^^
EDIT: Basically I want the abc_id = 1 to be ignored in the count because one of it's flag = 1. I hope it's clear enough :|
The main issue was figuring out what you meant, but I'm pretty sure it's this. :o)
Count the distinct abc_ids, fully excluding the ids that have at least one row that was flagged. So id 1 is excluded, because it has a flag.
You can solve this with a not in condition, to exclude all ids that were flagged. After that, you can count the remaining rows, using count(distinct abc_id), to get the number of distinct ids.
select
count(distinct abc_id) as non_flagged_id_count
from
xyz x1
where
abd_id not in
(select x2.abc_id from xyz x2 where x2.flag = 1)
select count(distinct abc_id)
from xyz as x
left join (
select abc_id from xyz where flag = 1
) as y using (abc_id)
where y.abc_id is null
;

MySQL select multiple values from one field

Not sure if this is possible but I have a schema like this:
id | user_id | thread_id
1 | 1 | 1
2 | 4 | 1
3 | 1 | 2
4 | 3 | 2
I am trying to retrieve the thread_id where user_id = 1 and 4. I know that in(1,4) does not fit my needs as its pretty much a OR and will pull up record 3 as well and Exists only returns a bool.
You may use JOIN (that answer already exists) or HAVING, like this:
SELECT
thread_id,
COUNT(1) AS user_count
FROM
t
WHERE
user_id IN (1,4)
GROUP BY
thread_id
HAVING
user_count=2
-check the demo. HAVING will fit better in case of many id's (because with JOIN you'll need to join as many times as many id you have). This is a bit tricky, however: you may do = comparison only if your records are unique per (user_id, thread_id); for example, your user_id can repeat, then use >=, like in this demo.
Try this with join, i guess you need to do AND operation with user_id must be 4 and 1 then
SELECT
t1.thread_id
FROM
TABLE t1
JOIN TABLE t2
ON (t1.user_id = t2.user_id)
WHERE t1.user_id = 1
AND t2.user_id = 4

how to find duplicate count without counting original

I need to count the number of duplicate emails in a mysql database, but without counting the first one (considered the original). In this table, the query result should be the single value "3" (2 duplicate x#q.com plus 1 duplicate f#q.com).
TABLE
ID | Name | Email
1 | Mike | x#q.com
2 | Peter | p#q.com
3 | Mike | x#q.com
4 | Mike | x#q.com
5 | Frank | f#q.com
6 | Jim | f#q.com
My current query produces not one number, but multiple rows, one per email address regardless of how many duplicates of this email are in the table:
SELECT value, count(lds1.leadid) FROM leads_form_element lds1 LEFT JOIN leads lds2 ON lds1.leadID = lds2.leadID
WHERE lds2.typesID = "31" AND lds1.formElementID = '97'
GROUP BY lds1.value HAVING ( COUNT(lds1.value) > 1 )
It's not one query so I'm not sure if it would work in your case, but you could do one query to select the total number of rows, a second query to select distinct email addresses, and subtract the two. This would give you the total number of duplicates...
select count(*) from someTable;
select count(distinct Email) from someTable;
In fact, I don't know if this will work, but you could try doing it all in one query:
select (count(*)-(count(distinct Email))) from someTable
Like I said, untested, but let me know if it works for you.
Try doing a group by in a sub query and then summing up. Something like:
select sum(tot)
from
(
select email, count(1)-1 as tot
from table
group by email
having count(1) > 1
)

specific mysql select query

today i need your help to get an specific sql select query.
i have following table:
and after a specific query regarding a specific id (in this case id 1) i wanna have a result like this:
user_id (an alias for the id_sender/id_recipient), date (maybe a max function, cause i wanna have the latest date to group), messages (a count function to the messages):
10 | 2012-01-14 09:10:05 | 4
11 | 2012-01-13 13:52:49 | 1
13 | 2012-01-13 20:01:17 | 1
14 | 2012-01-14 09:20:17 | 1
i tryed a lot but dont get the exact results - so my approach was something like this:
SELECT `id_recipient`, `id_sender`, MAX(`date`) AS `date`, COUNT(*) AS `messages` FROM `table` WHERE `id_recipient` = 1 OR `id_sender` = 1 GROUP BY `id_recipient`, `id_sender`
but then i get this result:
its not so bad but as u can see the 4th line should be included in the results of the first one.
i hope u got me. feel free to ask if smth is not clear.
thanks in advance,
greetings
Ok, so since we know the value for id_recipient, we can use some math to trick SQL into getting this nasty query done.
Let n be the id value of the person of interest.
We know that the pairing of id_recipient and id_sender will ALWAYS include the user with id value n. Based on the where clause.
Therefore, id_recipient + id_sender == n + id_otherPerson is true.
The resulting query will be very similar to this.
(It's been a while, but I don't think I have any syntax problems)
SELECT (`id_recipient` + `id_sender` - n) AS `id_otherPerson`,
MAX(`date`) AS `date`, COUNT(*) AS `messages`
FROM `table`
WHERE `id_recipient` = n XOR `id_sender` = n
GROUP BY `id_otherPerson`;
Edit: I've changed it to an XOR, so if person n messages person n, it won't cause all values to be incremented by the number of times n has messaged themself.
What about this?
SELECT user_id, MAX(date), COUNT(*)
FROM (
SELECT id_recipient AS 'user_id', date
FROM table
WHERE id_recipient <> 1 AND id_sender = 1
UNION
SELECT id_sender AS 'user_id', date
FROM table
WHERE id_recipient = 1 AND id_sender <> 1
) AS tbl
GROUP BY user_id
It assumes you want to use id_recipient if the id_sender is 1 and id_sender if id_recipient is 1.
I believe the output you want should be as below
10 | 2012-01-13 20:01:17 | 3
11 | 2012-01-13 13:52:49 | 1
13 | 2012-01-13 20:01:17 | 1
I'm saying as you are mixing id_recipient and id_sender