MySQl Query to select count until you hit a specific value - mysql

I have this table e.g.:
Id StatusDate Status
1 20-08-2014
1 15-08-2014
1 09-08-2014 P
2 17-08-2014
1 10-08-2014
2 12-08-2014
2 06-07-2014 P
1 30-07-2014
2 02-07-2014
2 01-07-2014 P
...... and so on
I want to select count by ID where status is blank until I hit the first 'P' in ascending order of date group by ID. So my results will be like this.
ID Count
1 3
2 2

Try it out. Not tested
SELECT t1.ID, count(*) FROM table t1
WHERE t1.StatusDate >= (SELECT MAX(t2.StatusDate) FROM table t2
WHERE t1.ID = t2.ID AND t2.Status = 'P')
GROUP BY t1.ID

Assuming your table name is StatusTable This will work:
SELECT
ID,
COUNT(*) AS `Count`
FROM StatusTable AS st
WHERE
st.Status = ''
AND st.StatusDate > (
SELECT st2.StatusDate
FROM `StatusTable` AS st2
WHERE st.ID = st2.ID
AND st2.Status = 'P'
ORDER BY st2.StatusDate DESC
LIMIT 1
)
GROUP BY st.ID
ORDER BY st.ID

One option is to use a JOIN and COUNT rows which have a lower statusdate value, like this:
SELECT t1.id, SUM(CASE WHEN t1.statusdate > t2.statusdate THEN 1 ELSE 0 END) AS mycount
FROM t t1 JOIN (
SELECT id, MIN(statusdate) statusdate
FROM t
WHERE status = 'P'
GROUP BY id
) t2
ON t1.id = t2.id
GROUP BY t1.id
Working Demo: http://sqlfiddle.com/#!2/d9d91/2

Related

Group SQL data and split into columns

I have a table with the following columns, where the timestamp column indicates the date the user viewed a product, and the purchase column if the view generated a purchase:
id
userId
productId
timestamp
purchase
1
2
4
2022-01-07
0
2
2
4
2022-01-10
1
3
2
4
2022-01-12
0
4
2
4
2022-01-16
1
How to group the data by userId and productId in order to return as follows:
id
userId
productId
firstView
lastView
firstPurchase
1
2
4
2022-01-07
2022-01-16
2022-01-10
For the firstView and listView columns I know I should use Min() and Max(), but the firstPurchase column I can't display. I tried using CASE when column purchase = 1 but it didn't work.
You need to use MIN(CASE WHEN purchase = 1 THEN "timestamp" ELSE NULL END), like this:
create table your_table (id int, userId int, productId int, "timestamp" date, purchase int)
insert into your_table (id, userId, productId, "timestamp", purchase)
values
(1,2,4,'2022-01-07 00:00:00',0),
(2,2,4,'2022-01-10 00:00:00',1),
(3,2,4,'2022-01-12 00:00:00',0),
(4,2,4,'2022-01-16 00:00:00',1);
SELECT
userId,
productId,
MIN(timestamp) AS firstView,
MAX(timestamp) AS lastView,
MIN(CASE WHEN purchase = 1 THEN "timestamp" ELSE NULL END) AS firstPurchase
FROM your_table
GROUP BY
userId,
productId;
p.s. please don't call a column "timestamp" :)
Try this:
SELECT t1.id, t1.userId, t1.productId, t1.timestamp,
(SELECT t3.timestamp
FROM yourtable t3
WHERE t3.userId = t1.userId
AND t3.productId = t1.productId
AND NOT EXISTS
(SELECT 1
FROM yourtable t4
WHERE t4.userId = t3.userId
AND t4.productId = t3.productId
AND t4.timestamp > t3.timestamp
)
) as lastView,
(SELECT t3.timestamp
FROM yourtable t3
WHERE t3.userId = t1.userId
AND t3.productId = t1.productId
AND t3.purchase = 1
AND NOT EXISTS
(SELECT 1
FROM yourtable t4
WHERE t4.userId = t3.userId
AND t4.productId = t3.productId
AND t4.timestamp < t3.timestamp
AND t4.purchase = 1
)
) as firstPurchase
FROM yourtable t1
WHERE NOT EXISTS
(SELECT 1 FROM yourtable t2
WHERE t1.userId = t2.userId
AND t1.productId = t2.productId
AND t2.timestamp < t1.timestamp)
See Sql Fiddle

Mysql - reorder / reset position key

I have a MySQL table with position key (65,000 records). I deleted, updated some rows in the middle of the table. Now I have, for example, something like this in the position 1 - 6 - 2 - 9
id
category
position
1
1
1
2
1
6
3
2
2
4
2
9
I want to reset / reorder the position key
id
category
position
1
1
1
2
1
2
3
2
1
4
2
2
How can I reset position where category = 1
and where category = 2?
Try this:
UPDATE source_table
JOIN ( SELECT id, ROW_NUMBER() OVER (PARTITION BY category ORDER BY position) position
FROM source_table ) subquery USING (id)
SET source_table.position = subquery.position
mysql> SHOW VARIABLES LIKE "%version%"; 5.7.24 – sagittarius
UPDATE source_table
JOIN ( SELECT t1.id, COUNT(t2.id) position
FROM source_table t1
JOIN source_table t2 ON t1.category = t2.category
AND t1.position >= t2.position
GROUP BY t1.id ) subquery USING (id)
SET source_table.position = subquery.position;
fiddle
if position is duplicated everything crashes – sagittarius
UPDATE source_table
JOIN ( SELECT t1.id, COUNT(t2.id) position
FROM source_table t1
JOIN source_table t2 ON t1.category = t2.category
AND ( t1.position > t2.position
OR ( t1.position = t2.position
AND t1.id >= t2.id ))
GROUP BY t1.id ) subquery USING (id)
SET source_table.position = subquery.position;
fiddle
UPDATE source_table t1
INNER JOIN
(
SELECT id,category,position, ROW_NUMBER() OVER (PARTITION BY category
ORDER BY position) position2
FROM source_table
) t2
ON t2.id = t1.id
SET
t1.position = t2.position2
I think this code is easy to understand and apply.

Retrieve last non-null record of every column for each record_id in MySQL

I have this MySQL table named records. Below is its contents.
id record_id Data1 Data2 Time
1 1 null 1 1/1/16
2 1 1 null 1/3/16
3 1 2 null 1/4/16
4 1 null 3 1/5/16
5 2 1 null 2/1/16
6 2 1 null 2/3/16
7 2 7 null 2/4/16
8 2 null 5 2/5/16
I would like to have a MySQL query to retrieve the last non-null record of each column for each record_id. The result would look something like;
record_id Data1 Data2 Time
1 2 3 1/5/16
2 7 5 2/5/16
The tricky part to this problem is that multiple columns are involved.
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT record_id, MAX(Time) AS Time
FROM yourTable
GROUP BY record_id
) t2
ON t1.record_id = t2.record_id AND
t1.Time = t2.Time
If you simply want the greatest value for the data and time columns, then see the answer given by #Matt. But your language makes it unclear what you really want.
Update:
Something like this might give the results you want:
SELECT a.record_id,
a.Data1,
b.Data2,
c.Time
FROM
(
SELECT t1.record_id,
t1.Data1
FROM yourTable t1
INNER JOIN
(
SELECT record_id,
MAX(CASE WHEN Data1 IS NULL THEN 0 ELSE id END) AS Data1Id
FROM yourTable
GROUP BY record_id
) t2
ON t1.record_id = t2.record_id AND
t1.Id = t2.Data1Id
) a
INNER JOIN
(
SELECT t1.record_id,
t1.Data2
FROM yourTable t1
INNER JOIN
(
SELECT record_id,
MAX(CASE WHEN Data2 IS NULL THEN 0 ELSE id END) AS Data2Id
FROM yourTable
GROUP BY record_id
) t2
ON t1.record_id = t2.record_id AND
t1.Id = t2.Data2Id
) b
ON a.record_id = b.record_id
INNER JOIN
(
SELECT t1.record_id,
t1.Time
FROM yourTable t1
INNER JOIN
(
SELECT record_id,
MAX(CASE WHEN Data2 IS NULL THEN 0 ELSE id END) AS TimeId
FROM yourTable
GROUP BY record_id
) t2
ON t1.record_id = t2.record_id AND
t1.Id = t2.TimeId
) c
ON a.record_id = c.record_id
Demo Here:
SQLFiddle
This one may solve your problem:
select
record_id,
substring_index(group_concat(Data1 order by Time desc), ',', 1) Data1,
substring_index(group_concat(Data2 order by Time desc), ',', 1) Data2,
substring_index(group_concat(Time order by Time desc), ',', 1) Time
from records
group by record_id
;
It may not be as fast as other answers, but is another version... give it a try. If you have a Data3 column in your table, you can copy/paste the Data1 column and just change all references of this column to the new one.
Just to explain how this works: the group_concat function concatenates all non-null values of a column with a separator (, by default). You can order the column before the concatenation. It works a bit like a window function in Oracle, Postgre, and others... The substring_index is just getting the first concatenated value, as the list is in a descending order of time.
it looks like you are just wanting the maximum data1, max data2, and max time which would be simple aggregation:
SELECT
record_id
,MAX(Data1) as Data1
,MAX(Data2) as Data2
,MAX(Time) as Time
FROM
yourTable
GROUP BY
record_id
SQL fiddle for it http://www.sqlfiddle.com/#!9/d95bc1/2
If latest non-null value per column is desired you can use:
SELECT t.record_id, MAX(t.Data1) as Data1, MAX(t.Data2) as Data2, MAX(t.Time) as Time
FROM
yourTable t
LEFT JOIN
(
SELECT
record_id, MAX(Time) as MaxTime
FROM
yourTable t
WHERE
Data1 IS NOT NULL
GROUP BY
record_id
) d1
ON t.record_id = d1.record_id
AND t.Time = d1.MaxTime
LEFT JOIN
(
SELECT
record_id, MAX(Time) as MaxTime
FROM
yourTable t
WHERE
Data2 IS NOT NULL
GROUP BY
record_id
) d2
ON t.record_id = d2.record_id
AND t.Time = d2.MaxTime
WHERE
d1.record_id IS NOT NULL
OR d2.record_id IS NOT NULL
GROUP BY
t.record_id
Using Tim's method you can actually still get to your results looking at the Latest Data1 record and then the latest Data2 record and then aggregating so they are not purely the MAX of everything but rather representative of the latest 2 records 1 for Data1 and 1 for Data2.
SQL fiddle for this part: http://www.sqlfiddle.com/#!9/d95bc1/10
I would like to have a MySQL query to retrieve the last non-null record of each column for each record_id.
Of course, what is still somewhat unclear is how you determine that a row is the last row, since rows in a database are by definition unordered.
So, my interpretation is that you want the last non-null Data1, Data2 and Time column values for each distinct record_id value. And a row value is considered last if it has a higher id value than another row value.
Assuming my understanding is correct, the following query would work:
select t.record_id,
(select t2.Data1
from tbl t2
where t2.record_id = t.record_id
and t2.Data1 is not null
order by t2.id desc
limit 1) as Data1,
(select t2.Data2
from tbl t2
where t2.record_id = t.record_id
and t2.Data2 is not null
order by t2.id desc
limit 1) as Data2,
(select t2.Time
from tbl t2
where t2.record_id = t.record_id
and t2.Time is not null
order by t2.id desc
limit 1) as Time
from tbl t
group by t.record_id
order by t.record_id
SQLFiddle Demo

Sum of two counts from different table with different conditions

Basically I need to merge these into one single query:
SELECT COUNT( DISTINCT id ) AS totalRows1
FROM other_events WHERE status = "approved"
AND Location = 1
SELECT COUNT( DISTINCT Id ) AS totalRows2
FROM core_events WHERE Status = "Active"
AND Location_id = 1
When I do it like below, if there is no event with Location_id = 1 query returns 0. In that condition I need it to return the count of the first table only.
SELECT COUNT( DISTINCT t1.id ) + COUNT( DISTINCT t2.Id ) AS total
FROM other_events AS t1, core_events AS t2
WHERE t1.status = "approved"
AND t1.Location = 1
AND t2.Location_id = 1
AND t2.Status = 'Active'
ps. column names are exactly like above
Use a UNION statement to merge the result like this:
SELECT SUM(total) FROM (
SELECT COUNT(DISTINCT id) AS total
FROM other_events
WHERE (status = "approved" AND Location = 1)
UNION ALL
SELECT COUNT(DISTINCT Id) AS total
FROM core_events
WHERE (Location_id = 1 AND Status = 'Active')
) union_result

MySQL select 1 row from multiple rows

Please Suggest to get single 0 type row from multiple (0 type rows) and selected row should be just before type 1 row
Emp_tbl (id,type,company_id,created_at)
1,0,121,2015-02-19 18:05
2,0,121,2015-02-19 18:15
3,0,121,2015-02-19 18:17
4,1,121,2015-02-19 19:22
5,2,121,2015-02-19 19:25
6,0,121,2015-02-19 22:05
7,0,121,2015-02-19 22:15
8,0,121,2015-02-19 22:17
9,1,121,2015-02-19 22:22
10,2,121,2015-02-19 22:25
Expected Result
3,0,121,2015-02-19 18:17
4,1,121,2015-02-19 19:22
5,2,121,2015-02-19 19:25
8,0,121,2015-02-19 22:17
9,1,121,2015-02-19 22:22
10,2,121,2015-02-19 22:25
So what you want is the MAX(Id) of the type = 0 rows for each row that has type = 1, where the Id is less. You can join and group to get that:
select max(t0.Id) Id
from Emp_tbl t1
join Emp_tbl t0 on t0.type = 0 and t0.Id < t1.Id
where t1.type = 1
group by t1.Id
The rest is just putting it together:
select *
from Emp_tbl
where type <> 0
union all
select t.*
from Emp_tbl t
join (
select max(t0.Id) Id
from Emp_tbl t1
join Emp_tbl t0 on t0.type = 0 and t0.Id < t1.Id
where t1.type = 1
group by t1.Id
) t0 on t.Id = t0.Id
SQL Fiddle demo