I have this sample table with Id, Grades:
Id Grades
1 50
1 60
1 70
1 40
1 80
1 65
2 80
2 67
2 100
2 90
2 60
etc
What I would like to do here is step by step:
Group them like this:
Between Assigned
1-50 D
51-70 C
71-90 B
91-100 A
and if the count of these groups is >=2, then assign the specified value above for each group:
if count(1-50)>=2 then D
if count(51-70)>=2 then C
if count(71-90)>=2 then B
if count(91-100)>=2 then A
so the output will be A,B,C,D or "". grouped by Id.
Thank you.
Try this query:
SELECT a.id, COALESCE(MAX(CASE
WHEN a.rank='D' AND a.cnt>=2 THEN 'D'
WHEN a.rank='C' AND a.cnt>=2 THEN 'C'
WHEN a.rank='B' AND a.cnt>=2 THEN 'B'
WHEN a.rank='A' AND a.cnt>=2 THEN 'A'
ELSE NULL
END),'') AS `rank`
FROM (
SELECT t.id,r.`rank`,COUNT(*) AS cnt
FROM (
SELECT 1 AS low, 50 AS high, 'D' AS `rank`
UNION ALL
SELECT 51,70,'C'
UNION ALL
SELECT 71,90,'B'
UNION ALL
SELECT 91,100,'A') r
INNER JOIN t t
ON t.grades>=r.low
AND t.grades<=r.high
GROUP BY t.id,r.`rank`) a
GROUP BY a.id
Related
I have a table
or_id
emp_id
cs
val
100
1
x
3.4
100
1
x
4.5
100
1
y
5
100
1
y
6
200
2
a
12
200
2
b
11
200
2
c
14
I want my output table like:
or_id
emp_id
CS1
CS2
CS3
100
1
x
y
200
2
a
b
c
I tried every possible code but nothing seems to work. I want dynamic code for this.
This query is working, but for larger dataset the execution time is lengthy, so I need an optimized code.
select distinct or_id,emp_id,
(select cs from (
select distinct cost_center from orl where emp_id=m.emp_id) a limit 1 offset 0 ) cs1,
(select cost_center from (
select distinct cost_center from orl where emp_id=m.emp_id) a limit 1 offset 1 ) cs2,
(select cost_center from (
select distinct cost_center from orl where emp_id=m.emp_id) a limit 1 offset 2 ) cs3
from orl m
For three CS columns, in MYSQL8
WITH
sorted AS
(
SELECT
or_id,
emp_id,
cs,
ROW_NUMBER() OVER (PARTITION BY or_id, emp_id ORDER BY cs) AS ordinal
FROM
your_table
GROUP BY
or_id,
emp_id,
cs
)
SELECT
or_id,
emp_id,
MAX(CASE WHEN ordinal = 1 THEN cs END) AS cs1,
MAX(CASE WHEN ordinal = 2 THEN cs END) AS cs2,
MAX(CASE WHEN ordinal = 3 THEN cs END) AS cs3
FROM
sorted
GROUP BY
or_id,
emp_id
Demo: https://dbfiddle.uk/2QNiXt6O
i would like to count the entries of my record table, filtered by a given period by valid_from and valid_till or if valid_till is null
Records
id
name
valid_from
valid_till
1
A
2022-07-01
2022-07-05
2
B
2022-07-02
2022-07-05
3
C
2022-07-02
2022-07-04
4
D
2022-07-03
null
5
E
2022-07-03
2022-07-03
Now let's assume that I get the following period (2022-06-30 to 2022-07-07) from the client. Now I want to find out all records that match this time period and the number of hits for each date in this period, not only for valid_from and valid_till.
I expect the following results:
date
matches
2022-06-30
0
2022-07-01
1
2022-07-02
3
2022-07-03
5
2022-07-04
4
2022-07-05
3
2022-07-06
0
2022-07-07
0
Furthermore, I would like to extend the whole scenario by knowing how many matches have already been read by the current user. For this, there is an additional table that holds the information per user and per record.
Viewstates
id
record
user
1
1
X
2
1
Y
3
2
X
4
3
Y
5
4
X
6
4
Y
7
4
Z
8
5
X
9
5
Y
10
5
Z
Assuming I am now user X then I would expect the following result:
date
matches
already_read_by_me
2022-06-30
0
0
2022-07-01
1
1
2022-07-02
3
2
2022-07-03
5
4
2022-07-04
4
3
2022-07-05
3
3
2022-07-06
0
0
2022-07-07
0
0
Unfortunately, I currently really have no idea how to implement and solve this.
My actually coding attempt looks like that
SELECT
COUNT(r.id) as matches,
COUNT(vs.record_id) as already_ready_by_me
FROM
records r
LEFT JOIN
(
SELECT id AS viewstate, record_id FROM viewstates WHERE user = 'xxx'
) vs ON r.id = vs.record_id
WHERE
r.valid_from >= '2022-07-01'
AND (r.valid_till <= '2022-07-10' OR r.valid_till IS NULL)
ORDER BY
valid_from
DESC;
Hope here is someone who can help me.
Thanks a lot.
BR,
Sven
The first thing you will need to do is to concatenate the input dates as in the selected_range subquery.
In my understanding the following query is the correct one, because if a record has valid_till = null it means that it is still valid until NOW right? So in your example, 2022-07-06 and 2022-07-07 would be a match, because record D would be valid from 2022-07-03 to NOW
############## ONLY MATCHES
SELECT selected_range.date,
COUNT(records.id) matches
FROM ( SELECT '2022-06-30' date
UNION ALL SELECT '2022-07-01'
UNION ALL SELECT '2022-07-02'
UNION ALL SELECT '2022-07-03'
UNION ALL SELECT '2022-07-04'
UNION ALL SELECT '2022-07-05'
UNION ALL SELECT '2022-07-06'
UNION ALL SELECT '2022-07-07') selected_range
LEFT JOIN records ON selected_range.date BETWEEN records.valid_from
AND COALESCE (records.valid_till, CAST(NOW() AS DATE))
GROUP BY selected_range.date
############# MATCHES READ BY USER
SELECT selected_range.date,
COUNT(records.id) matches,
COUNT(viewstates.USER) already_read_by_me
FROM ( SELECT '2022-06-30' date
UNION ALL SELECT '2022-07-01'
UNION ALL SELECT '2022-07-02'
UNION ALL SELECT '2022-07-03'
UNION ALL SELECT '2022-07-04'
UNION ALL SELECT '2022-07-05'
UNION ALL SELECT '2022-07-06'
UNION ALL SELECT '2022-07-07') selected_range
LEFT JOIN records ON selected_range.date BETWEEN records.valid_from
AND COALESCE (records.valid_till, CAST(NOW() AS DATE))
LEFT JOIN viewstates ON viewstates.record = records.id
AND viewstates.USER = 'X'
GROUP BY selected_range.date
But your expected result is possible, however the logic that i was able to notice was that for the record with valid_till=null be a match, there must be other record that has valid_till NOT null. For example, for the 2022-07-05, the D record matched, because the records A and B also matched.
But for the 2022-07-06 and 2022-07-07 the record D was not considered, i understood that it was because there are not other records that matched.
So I implemented this logic:
######## ONLY MATCHES
SELECT selected_range.date,
COUNT(records.id) matches
FROM ( SELECT '2022-06-30' date
UNION ALL SELECT '2022-07-01'
UNION ALL SELECT '2022-07-02'
UNION ALL SELECT '2022-07-03'
UNION ALL SELECT '2022-07-04'
UNION ALL SELECT '2022-07-05'
UNION ALL SELECT '2022-07-06'
UNION ALL SELECT '2022-07-07') selected_range
LEFT JOIN records ON (selected_range.date BETWEEN records.valid_from
AND COALESCE (records.valid_till, records.valid_from))
OR (EXISTS (SELECT 1
FROM records records_inner
WHERE (selected_range.date BETWEEN records_inner.valid_from
AND records_inner.valid_till)
AND selected_range.date >= records.valid_from
AND records.valid_till IS NULL
))
GROUP BY selected_range.date
######## MATCHES READ BY THE USER
SELECT selected_range.date,
COUNT(records.id) matches,
COUNT(viewstates.user) already_read_by_me
FROM ( SELECT '2022-06-30' date
UNION ALL SELECT '2022-07-01'
UNION ALL SELECT '2022-07-02'
UNION ALL SELECT '2022-07-03'
UNION ALL SELECT '2022-07-04'
UNION ALL SELECT '2022-07-05'
UNION ALL SELECT '2022-07-06'
UNION ALL SELECT '2022-07-07') selected_range
LEFT JOIN records ON (selected_range.date BETWEEN records.valid_from
AND COALESCE (records.valid_till, records.valid_from))
OR (EXISTS (SELECT 1
FROM records records_inner
WHERE (selected_range.date BETWEEN records_inner.valid_from
AND records_inner.valid_till)
AND selected_range.date >= records.valid_from
AND records.valid_till IS NULL
))
LEFT JOIN viewstates ON viewstates.record = records.id
AND viewstates.USER = 'X'
GROUP BY selected_range.date
I have table with the following:
ID TYPE ProjectID Date Rev
1 A 1 30-1-2010 500
2 B 1 28-02-2011 580
2 B 2 30-04-2011 540
2 B 3 03-04-2019 440
Results:
ID TYPE ProjectID Date Rev
1 A 1 30-1-2010 500
1 A 2 01-01-2000 0
1 A 3 01-01-2000 0
2 B 1 28-02-2011 580
2 B 2 30-04-2011 540
2 B 3 03-04-2019 440
I want to write an SQL query in which, whenever there is Type “A”, two rows should automatically be inserted with project id 2 and 3 and with default Date and Rev data.
Currently, I am using UNION function to add this data manually, but I want to do it automatically.
I am not sure how to do this in SQL.
If I understand correctly:
select it.id, it.type, p.projectid,
coalesce(t.date, '2000-01-01') as date, coalesce(t.rev, 0) as rev
from (select distinct id, type from t) it cross join
(select distinct projectid from t) p left join
t
on t.id = it.id and t.type = it.type and t.projectid = p.projectid;
This fills in the missing values, so all id/type combinations have all projects.
Have to show on specific count . Means i have to show max score value 4 times then less than that score to 3 times and so on
I have table like:-
ID Score
1 1
2 1
3 1
4 1
5 2
6 2
7 2
8 3
9 3
10 4
11 4
12 4
and I am expecting output like:-
Score
1
1
1
2
2
3
4
Try this:
SELECT Score
FROM
(SELECT a.Score, a.ID, count(*) as RN FROM TableName a
JOIN TableName b ON a.Score = b.Score AND a.ID >= b.ID
GROUP BY a.Score, a.ID) T
WHERE RN>1
Result:
SCORE
1
1
1
2
2
3
4
4
An example in SQL Fiddle.
Explanation:
In the inner query, we are selecting a row number partitioned by ID and order by score. And then we select Score from the inner query whose Row number is greater than 1 (inorder to omit 1 record).
SELECT x.*
FROM my_table x
JOIN my_table y
ON y.marks = x.marks
AND y.id <= x.id
GROUP
BY x.marks
, x.id
HAVING COUNT(0) <= MAX(4-x.marks)
ORDER
BY marks DESC,id;
I have data in a table like this:
fgid qty ntid
1 100 10
2 90 10
6 200 11
1 80 11
1 120 12
6 100 12
6 30 13
And i make query :
SELECT fgid, SUM(qty) AS total_qty, COUNT(ntid) AS nt_count FROM sofg
GROUP BY fgid
AND the result is :
fgid total_qty nt_count
1 300 3
2 90 1
6 330 3
Then i want to make the result like this :
no fgid total_qty nt_count
1 1 300 3
2 2 90 1
3 6 330 3
How to do that with a query? where 'no' is (like) autoincrement number.
Try this query.
SELECT
#rownum := #rownum + 1 rownum,
t.*
FROM (SELECT #rownum:=0) r,
(
SELECT fgid, SUM(qty) AS total_qty, COUNT(ntid) AS nt_count FROM sofg GROUP BY fgid
) t;
Basically the same as Dhinakaran's answer, but there's no need to put the whole main query into a subquery. There's no difference to his answer appart from maybe being more pleasing to the eye, but please accept Dhinakaran's answer, as he was faster.
SELECT
#rownum:=#rownum + 1 as rownumber,
fgid,
SUM(qty) AS total_qty,
COUNT(ntid) AS nt_count
FROM sofg
, (select #rownum:=0) v
GROUP BY fgid