First Unique Sql row - mysql

I have a MySql table of users order and it has columns such as:
user_id | timestamp | is_order_Parent | Status |
1 | 10-02-2020 | N | C |
2 | 11-02-2010 | Y | D |
3 | 11-02-2020 | N | C |
1 | 12-02-2010 | N | C |
1 | 15-02-2020 | N | C |
2 | 15-02-2010 | N | C |
I want to count number of new custmer per day defined as: a customer who orders non-parent order and his order status is C AND WHEN COUNTING A USER ONCE IN A DAY WE DONT COUNT HIM FOR OTHER DAYS
An ideal resulted table will be:
Timestamp: Day | Distinct values of User ID
10-02-2020 | 1
11-02-2010 | 1
12-02-2010 | 0 <--- already counted user_id = 1 above, so no need to count it here
15-02-2010 | 1
table name is cscart_orders

If you are running MySQL 8.0, you can do this with window functions an aggregation:
select timestamp, sum(timestamp = timestamp0) new_users
from (
select
t.*,
min(case when is_order_parent = 'N' and status = 'C' then timestamp end) over(partition by user_id) timestamp0
from mytable t
) t
group by timestamp
The window min() computes the timestamp when each user became a "new user". Then, the outer query aggregates by date, and counts how many new users were found on that date.
A nice thing about this approach is that it does not require enumerating the dates separately.

You can use two levels of aggregation:
select first_timestamp, count(*)
from (select t.user_id, min(timestamp) as first_timestamp
from t
where is_order_parent = 'N' and status = 'C'
group by t.user_id
) t
group by first_timestamp;

Related

Get the last know value for a specific reference in inconsistent timeline

I'm trying to get records from the stats table and if there is no data for the specific day and reference get the latest known value for a given ref.
stats table:
| ref | date | views |
| --- | -------- | ----- |
| 1 |2022-01-01|1 |
| 2 |2022-01-01|1 |
| 1 |2022-01-02|2 |
| 2 |2022-01-02|1 |
| 1 |2022-01-03|2 |
| 1 |2022-01-04|3 |
| 2 |2022-01-04|3 |
As you see above there the record for ref 2 at 2022-01-03 is missing.
Now I want to sum views for those records and group them by the ref column and since there is one missing record for ref 2 the value for the summation should be taken from the latest record (2022-01-02).
I have also the posts table:
| id | title |
| --- | --------- |
| 1 |title no. 1|
| 2 |title no. 2|
Also, I have to create a timeline from the oldest stat to the current date.
What do I have is:
WITH RECURSIVE timeline (
date
) AS (
SELECT
MIN(date)
FROM
stats
UNION ALL
SELECT
DATE_ADD(date, INTERVAL 1 day)
FROM
timeline
WHERE (timeline.date < CURRENT_DATE),
posts_days AS (
SELECT timeline.date, posts.id
FROM
posts
CROSS JOIN timeline
),
view_stats AS (
SELECT posts_days.date, posts_days.id, stats.views
FROM
posts_days
LEFT JOIN stats ON (stats.ref = posts_days.id and stats.date = posts_days.date)
)
SELECT
view_stats.date, SUM(view_stats.views) AS views
-- ,SUM(prev_stats.views) AS prev_views,
FROM view_stats
LEFT JOIN (
SELECT id, (view_stats.views) as views, date FROM view_stats GROUP BY id, date
) as prev_stats on prev_stats.date = (
SELECT date FROM view_stats s1
WHERE s1.date < view_stats.date and s1.id = view_stats.id ORDER BY date desc limit 1
) and prev_stats.id = view_stats.id
GROUP BY date
ORDER BY date
But it obviously behaves in the wrong way.
I would be appreciated any tips on how to solve this one.

Mysql Count row data by every date but only have few data date

I have a table "activity" like this
idEmployee | activity | Date
1 | a | 2019/01/01
1 | b | 2019/01/01
2 | c | 2019/01/01
2 | d | 2019/01/01
1 | e | 2019/01/02
2 | f | 2019/01/03
1 | f | 2019/01/03
3 | c | 2019/01/01
4 | d | 2019/01/03
1 | e | 2019/01/02
2 | f | 2019/01/03
and i want to count every date from 2019/01/01 - 2019/01/03 that has no activity by every idEmpolyee (as total_no_actitivity) like this
idEmployee | total_no_activity
1 | 0
2 | 1 (2019/01/02
3 | 2 (2019/01/02,2019/01/03)
4 | 2 (2019/01/01,2019/01/02)
but i only can select idemployee that has no activity , without count total_no_activity.
SELECT idEmployee, namaLengkap, date
FROM account LEFT JOIN timesheet USING (idEmployee)
WHERE NOT EXISTS (SELECT idEmployee
FROM timesheet
WHERE account.idEmployee = timesheet.idEmployee AND weekday(date) AND date between '2019/08/05' and '2019/08/09' AND idrole = '4' AND statusaktif = '1' )
ORDER BY idEmployee ASC
is it possible to count total_no_activity with table "activity" only?
SELECT idEmployee,
3 - COUNT(DISTINCT `Date`) total_no_activity
FROM account
WHERE `Date` BETWEEN `2019/01/01` AND `2019/01/03`
GROUP BY idEmployee
where 3 is the amount of days in the period if interest, inclusive.
If some idEmployee have no records at all in the period in interest then this value will not be listed in output.
unfortunately i need the idEmployee that have no records will be listed in the output
Assiming that you need all idEmployee values which are present in source table at least once (maybe even out of the period in interest) use
SELECT account.idEmployee,
3 - COUNT(DISTINCT account.`Date`) total_no_activity
FROM (SELECT DISTINCT idEmployee FROM account) all_employees
LEFT JOIN account USING (idEmployee)
WHERE account.`Date` BETWEEN `2019/01/01` AND `2019/01/03`
GROUP BY account.idEmployee
I would suggest:
select a.idEmployee,
(datediff(params.date2, params.date1) + 1 -
count(distinct ac.date)
) as missing_days
from (select date('2019-01-01') as date1, date('2019-01-03') as date2
) params cross join -- a convenience so we don't have to retype the constants
accounts a left join
activity ac
on ac.idEmployee = a.idEmployee and
ac.date >= params.date1 and
ac.date <= params.date2
group by a.idEmployee;
To prevent typos and to allow the dates to change easily, this introduces a subquery, params, that has the date values.

Return latest entry result providing there is a review or close entry

I have a table that holds the answers to a question which is asked at entry to the system, at review periods and then at closure. The client can be opened and closed multiple times during their life on the system.
I am trying to get the latest 'entry' result from the table which also has either an associated 'review' or 'close' result.
This is my table (I have just included 1 user but the actual table has thousands of users):
row | user_id | answer | type | date_entered |
----+---------+--------+--------+--------------+
1 | 12 | 3 | entry | 2016-03-13 |
2 | 12 | 1 | review | 2016-03-14 |
3 | 12 | 7 | review | 2016-03-16 |
4 | 12 | 7 | close | 2016-03-17 |
5 | 12 | 8 | entry | 2016-03-20 |
6 | 12 | 2 | review | 2016-03-21 |
7 | 12 | 3 | close | 2016-03-22 |
8 | 12 | 1 | entry | 2016-03-28 |
So for this table the query would just return row 5 because the 'entry' on row 8 doesn't have any 'review' or 'closure' records after it.
Hopefully that makes sense.
SELECT a.*
FROM my_table a
JOIN
( SELECT x.user_id
, MAX(x.date_entered) date_entered
FROM my_table x
JOIN my_table y
ON y.user_id = x.user_id
AND y.date_entered > x.date_entered
AND y.type IN ('review','close')
WHERE x.type = 'entry'
GROUP
BY x.user_id
) b
ON b.user_id = a.user_id
AND b.date_entered = a.date_entered;
Basically you can seperate your query into two sub-queries. First query should get lastest record id (review and closure). Second query should have row_id > found_id.
SELECT *
FROM my_table
WHERE type = 'entry'
AND row_id > (SELECT Max(row_id)
FROM my_table
WHERE ( type = 'review'
OR type = 'close' ))
Please be careful about that; subquery may return zero-set.
I could think of several ways of doing it. But first a note: your date_entered field seems to be just a date. To tell which occurs "later" I'm going to use row because e.g. if both entry and review occurred on the same date, it's not possible to tell from the date_entered which one was later.
I just list a couple of solutions. The first one might be more efficient, but you should measure.
Here's a join against a subquery:
SELECT
m1.*
FROM
mytable m1
JOIN (SELECT
row, user_id
FROM
mytable
WHERE
type IN ('review', 'close') AND
user_id = 12
ORDER BY row DESC LIMIT 1) m2 ON m1.user_id = m2.user_id
WHERE
m1.user_id = 12 AND
m1.row < m2.row
ORDER BY
row DESC LIMIT 1
Here's a subquery for max:
SELECT
*
FROM
mytable
WHERE
row = (SELECT
MAX(m1.row)
FROM
mytable m1,
mytable m2
WHERE
m1.user_id = m2.user_id AND
m1.type = 'entry' AND
m2.type IN ('review', 'close') AND
m1.row < MAX(m2.row))

select two tables mysql without join

There are two tables, recharge and purchase.
select * from recharge;
+-----+------+--------+---------------------+
| idx | user | amount | created |
+-----+------+--------+---------------------+
| 1 | 3 | 10 | 2016-01-09 20:16:18 |
| 2 | 3 | 5 | 2016-01-09 20:16:45 |
+-----+------+--------+---------------------+
select * from purchase;
+-----+------+----------+---------------------+
| idx | user | resource | created |
+-----+------+----------+---------------------+
| 1 | 3 | 2 | 2016-01-09 20:55:30 |
| 2 | 3 | 1 | 2016-01-09 20:55:30 |
+-----+------+----------+---------------------+
I want to figure out balance of users which is SUM(amount) - COUNT(purchase.idx). (in this case, 13)
So I had tried
SELECT (SUM(`amount`)-COUNT(purchase.idx)) AS balance
FROM `recharge`, `purchase`
WHERE purchase.user = 3 AND recharge.user = 3
but, it returned error.
If you want an accurate count, then aggregate before doing arithmetic. For your particular case:
select ((select sum(r.amount) from recharge where r.user = 3) -
(select count(*) from purchase p where p.user = 3)
)
To do this for multiple users, move the subqueries to the from clause or use union all and aggregation. The second is safer if a user might only be in one table:
select user, coalesce(sum(suma), 0) - coalesce(sum(countp), 0)
from ((select user, sum(amount) as suma, null as countp
from recharge
group by user
) union all
(select user, null, count(*)
from purchase
group by user
)
) rp
group by user
It is possible to using union like this
SELECT SUM(`amount`-aidx) AS balance
FROM(
SELECT SUM(`amount`) as amount, 0 as aidx
from `recharge` where recharge.user = 3
union
select 0 as amount, COUNT(purchase.idx) as aidx
from `purchase`
WHERE purchase.user = 3 )a

Condition row total as a column in another table using MySQL

Firstly, I apologize for the terrible wording, but I'm not sure how to describe what I'm doing...
I have a table of computer types (id, type, name), called com_types
id | type | name
1 | 1 | Dell
2 | 4 | HP
In a second table, I have each individual computer, with a column 'type_id' to denote what type of computer it is, called com_assets
id | type_id | is_assigned
1 | 4 | 0
2 | 1 | 1
I'd like to create a view that shows each computer type, and how many we have on hand and in use, and a total, so the outcome would be
id | type | name | on_hand | in_use | total |
1 | 1 | Dell | 0 | 1 | 1 |
2 | 4 | HP | 1 | 0 | 1 |
As you can see, the on_hand, in_use, and total columns are dependent on the type_id and is_assigned column in the second table.
So far I have tried this...
CREATE VIEW test AS
SELECT id, type, name,
( SELECT COUNT(*) FROM com_assets WHERE type_id = id AND is_assigned = '0' ) as on_hand,
( SELECT COUNT(*) FROM com_assets WHERE type_id = id AND is_assigned = '1' ) as in_use,
SUM( on_hand + in_use ) AS total
FROM com_types
But all this returns is one column with all correct values, except the total equals ALL of the computers in the other table. Will I need a trigger to do this instead?
on_hand is the count of assigned = 0, and in_use is the count of assigned = 1. You can count them together, without the correlated subqueries, like this:
SELECT
com_types.id,
com_types.type,
com_types.name,
COUNT(CASE WHEN com_assets.is_assigned = 0 THEN 1 END) AS on_hand,
COUNT(CASE WHEN com_assets.is_assigned = 1 THEN 1 END) AS in_use,
COUNT(*) AS total
FROM com_types
JOIN com_assets ON com_types.id = com_assets.id
GROUP BY
com_types.id,
com_types.type,
com_types.name