Kind of simple query in mysql but can't solve it - mysql

I previously posted a question here: Mysql query with joined tables problems
but didn't get good answers, so I thought I break it down to show only the part that gives me a headache, I might get answers faster to this question, and if I can solve this, I can solve the previous problem too.
The values are referring to an item_id, and I want to get item_id's where the item is referring to (('good' OR 'bad') AND 'fast')
So item_id 1 must be listed, because it is good and also fast
item_id 2 should not listed because it is not fast.
The result i want to get
item_id
1
5
if I have a table like this
id item_id value
1 1 'good'
2 1 'fast'
3 2 'good'
4 2 'slow'
5 3 'good'
6 3 'slow'
7 4 'bad'
8 4 'slow'
9 5 'bad'
10 5 'fast'
11 6 'moderate'
12 6 'fast'
Can someone help me?

One way to see this is: you want a statement per item, so you aggregate and group by item. Criteria come in the HAVING clause.
select item_id
from mytable
group by item_id
having count(case when value = 'fast' then 1 end) >= 1
and count(case when value in ('good','bad') then 1 end) >= 1;
(You can also use sum(case when value = 'fast' then 1 else 0 end) >= 1 or max(case when value = 'fast' then 1 else 0 end) = 1 or some expression along those lines. COUNT(expression) counts expressions that are not null. As I am omitting an ELSE branch, a non-matching record results in null and is thus not counted. Some prefer SUM, some prefer COUNT - it's finally a matter of personal preference.)

One way to do this is with an EXISTS query.
Here's an example:
select t1.item_id
from your_table t1
where t1.value = 'fast'
and exists (
select NULL
from your_table t2
where t2.item_id = t1.item_id
and t2.value in ('good','bad')
);

Considering that a item can be good or bad, not both, I would use the simple way:
select item_id your_table
where value in ('good','bad','fast')
group by item_id
having count(*) = 2
count should be always 2 in those cases.

Related

MySQL count and case with one to many relationship

My first question here.
I got 2 tables with one to many relationship:
policies
id
issue_date
user_id
insurance_type
1
1-2-2021
100
apartment
2
1-2-2021
200
car
policy_details
id
policy_id
type
1
1
type A
1
1
type A
2
1
type B
3
1
type C
1 policy record can have many policy_details records.
I need to count the policies rows with some case,
and I need to join the policy_details as well because I count them also.
Example to simple query:
SELECT
user_id,
COUNT(CASE WHEN `insurance_type` = 'apartment' THEN 1 ELSE NULL END) as totalApartmentType,
COUNT(CASE WHEN policy_details.type = 'typeA' THEN 1 ELSE NULL END) as totalTypeA
FROM `policies`
JOIN `policy_details` ON policy_details.policy_id = policies.id
AND MONTH(`issue_date`) = 2
GROUP BY (`user_id`)
The problem here is that if 1 policy connected to 2 policy_details records for example,
the count will be 2 and it should be 1, cause I need to count the policy record, not the policy_details joined records.
And if policy_details has 5 records connected to policy, the count will be 5. (should be 1).
The result I need for the query above:
user_id
totalApartmentType
totalTypeA
100
1
2
Can it be done guys?
You can use COUNT(DISTINCT) making sure you generate the id instead of 1 in case the condition is fulfilled:
COUNT(DISTINCT CASE WHEN insurance_type = 'apartment'
THEN policies.id
ELSE NULL
END) AS totalApartmentType,

MySQL: how to query to show answers of two users side by side

I have a table with some rows
questionId userId answers
1 1 1
2 1 0
3 1 1
4 1 1
1 2 1
2 2 1
3 2 0
4 2 0
I want this result.
questionId user1 user2
1 1 1
2 0 1
3 1 0
4 1 0
I don't know how I can get this. Of course I can query each user's answers and merge it with javascript, but I'm curious whether I can get that result with one query. Do I have to use group by or subquery?
You can accomplish this by aggregating your table by question and pivoting on the user:
SELECT
questionId,
MAX(CASE WHEN userId = 1 THEN answers END) AS user1,
MAX(CASE WHEN userId = 2 THEN answers END) AS user2
FROM
yourTable
GROUP BY
questionId
Concepually the way the query works is to take groups of question records, and for each group extract out the answers for each user. For example, for the first user we take the max of either the answer for user one, or NULL if not user one. Since NULL values are ignored by max, this just yields the answer we want. The longer form of the CASE expression I wrote is this:
MAX(CASE WHEN userId = 1 THEN answers ELSE NULL END)
So while aggregating a given question we would conceptually have MAX of the user one answer and a bunch of null values, the latter which would be ignored.
Demo

sql calculate percentage over grouped data

I have a table X like this,
student ans_status question_id
1 1 10
2 -1 10
3 1 10
4 0 10
1 -1 11
2 1 11
3 -1 11
4 -2 11
expected o/p is
10 2/3
11 1/3
etc..
Now, i want the data fro each question 10 like,
number of 1's/(total of 1's and -1's for each question)
I have tried this,
select (select count(student_id) from X
where question_id=10 and ans_status=1) / count(student_id)
from X
where question_id=10
group by ans_status
having ans_status in(1,-1).
i can do it in a nested query, by again selecting and grouping according to the status condition, but is there any way better to do this?
please note i want this for all questions in the table
You can just do:
select question_id,
avg(ans_status = 1)
from X
where ans_status in (1, -1)
group by question_id;
This uses the MySQL feature that a boolean expression is treated as an integer in a numeric context. "True" is 1 and "false" is 0, so the average turns out to be the percentage that are true.
If you want the values independently:
select question_id,
sum(ans_status = 1), count(*)
from X
where ans_status in (1, -1)
group by question_id;
Use GROUP BY for taking count of each question_id for getting count of answer_id is 1 or -1.
Query
select t.`question_id`,
t.`count_1` / t.`total_count` as `new_col` from(
select `question_id`,
sum(case `ans_status` when 1 then 1 when -1 then 1 else 0 end) as `count_1`,
count(*) as `total_count`
from `your_table_name`
group by `question_id`
)t;
Find a demo here

MySQL - add column to table and insert "tag" if order is from new customer

I have simple table:
Order_ID Client_ID Date Order_Status
1 1 01/01/2015 3
2 2 05/01/2015 3
3 1 06/01/2015 3
4 2 10/01/2015 3
5 1 12/01/2015 4
6 1 05/02/2015 3
I want to identify orders from new customers which are orders in same month in which that customer made first order with Order_Status = 3
So the output table should look like this:
Order_ID Client_ID Date Order_Status Order_from_new_customer
1 1 01/01/2015 3 yes
2 2 05/01/2015 3 yes
3 1 06/01/2015 3 yes
4 2 10/01/2015 3 yes
5 1 12/01/2015 4 NULL
6 1 05/02/2015 3 no
I wasn't able to successfully figure out the query. Thanks a lot for any help.
Join with a subquery that gets the date of the first order by each customer.
SELECT o.*, IF(MONTH(o.date) = MONTH(f.date) AND YEAR(o.date) = YEAR(f.date),
'yes', 'no') AS order_from_new_customer
FROM orders AS o
JOIN (SELECT Client_ID, MIN(date) AS date
FROM orders
WHERE Order_Status = 3
GROUP BY Client_ID) AS f
ON o.Client_ID = f.Client_ID
Use a CASE statement along with a SELF JOIN like below
select t1.*,
case when t1.Order_Status = 3 and MONTH(t1.`date`) = 1 then 'yes'
when t1.Order_Status = 3 and MONTH(t1.`date`) <> 1 then 'no'
else null end as Order_from_new_customer
from order_table t1 join order_table t2
on t1.Order_ID < t2.Order_ID
and t1.Client_ID = t2.Client_ID;
If your order table gets big, the solutions from Rahul and Barmar will tend to get slow.
I would hope your shop will get many orders and you will run into performance trouble ;-). So I would suggest marking the very first order of a new customer with a tinyint column, and when you have the comfort of a tinyint, you could code it like:
0 : unknown
1 : very first order
2 : order in first month
3 : order in "grown-up" mode.
The very first order you could probably mark easily, everyone loves a bright new customer enough to store this event somehow during first ordering. The other orders you can identify in a background job / cronjob by there "0" for unknown, or you mark your old customers and store the "3" on their orders.
The result-set can be achieved without any table-join or subquery:
select
if(Order_Status<>3,null,if(#first_date:=if(#prev_client_id!=Client_ID,month(date),#first_date)=month(date),"yes","no")) as Order_from_new_customer
,Order_ID,Client_ID,date,Order_Status,#prev_client_id:=client_id
from
t1,
(select #prev_client_id:="",#first_date:="")t
order by Client_ID ,date
One extra column added for computation and order by clause is used.
Verify result at http://sqlfiddle.com/#!9/83c29f/24

Query return 1 row when no data present

I am running several instances of the same query. The below version should not be returning any data but I am getting 1 row with 'null' in each of the columns:
SELECT (CASE ScanName WHEN 'SYSTEM-HQ' THEN 'HQ
System' END) AS System,
sum(CASE pspplMSSeverity WHEN 1 THEN 10 WHEN 2 THEN 9 WHEN
3 THEN 6 WHEN 4 THEN 3 END) AS Score,
(sum(CASE pspplMSSeverity WHEN 1 THEN 10 WHEN 2 THEN 9
WHEN 3 THEN 6 WHEN 4 THEN 3 END)/COUNT(pspplMSSeverity)) AS
Grade
FROM missingpatches
WHERE ScanName like '%SYSTEM-HQ%'
ORDER BY LAST_UPDATE DESC LIMIT 1
How can I modify this query to ensure that I am only returning data when valid values exist?
Could this be due to the use of the Case and Sum within the primary SQL statement that are causing the Null data to be returned?
SELECT (CASE ScanName WHEN 'SYSTEM-HQ' THEN 'HQ
System' END) AS System,
sum(CASE pspplMSSeverity WHEN 1 THEN 10 WHEN 2 THEN 9 WHEN
3 THEN 6 WHEN 4 THEN 3 END) AS Score,
(sum(CASE pspplMSSeverity WHEN 1 THEN 10 WHEN 2 THEN 9
WHEN 3 THEN 6 WHEN 4 THEN 3 END)/COUNT(pspplMSSeverity)) AS
Grade
FROM missingpatches
WHERE ScanName like '%SYSTEM-HQ%'
HAVING System IS NOT NULL # Added
ORDER BY LAST_UPDATE DESC LIMIT 1
Try add a HAVING.
Most probably the table missingpatches contains 1 row that satisfy
WHERE ScanName like '%SYSTEM-HQ%'
i.e. a row with ScanName that contains 'SYSTEM-HQ' but not exactly equal to 'SYSTEM-HQ' which you are equating with in the select's 1st col.
In SQL, the columns are evaluated after tables are joined and where clauses are evaluated. Hence, the row you are seeing, is actually meeting the where clause criteria.