How to run sql query recursively? - mysql

For example, there is a accounts table has:
account_id | ......
000 | ......
001 | ......
004 | ......
010 | ......
.....
198 | ......
I want to get the distribution of account id, instead of running following query again and again, is there any smarter way to get id count for 000-010, 010-020, ..., 190-200? Thanks
SELECT count(account_id)
FROM accounts
WHERE account_id >= '000' AND account_id <= '010';

You can divide the account_id by 10 to create a range and then group by the divided result to get the result you want:
SELECT CONCAT(LPAD(FLOOR(account_id/10)*10,3, '0'), '-', LPAD(FLOOR(account_id/10)*10+9, 3, '0')) AS `range`,
COUNT(*) AS number
FROM accounts
GROUP BY `range`
Output (for some sample data in my demo):
range number
000-009 3
010-019 2
020-029 1
030-039 1
040-049 1
050-059 2
Demo on dbfiddle

You would use group by:
select (case when account_id >= '000' and account_id <= '010' then '000-010'
when account_id >= '011' and account_id <= '020' then '011-020'
when account_id >= '021' and account_id <= '030' then '021-030'
. . .
end) as account_id_grp,
count(*)
from accounts
group by account_id_grp
order by account_id_grp;

select t1.account_id ||'-'||t2.
account_id,count(*) from
table t1 where account_id IN (Select account_id from
table t2 where t2.account_id-t1.account_id=10)`
I tried like taking difference of the account ids in the tables via corelated subquery

Related

get the total count but exclude certain condition

Hello I had this table:
id | user_id | status
1 | 34 | x
2 | 35 | x
3 | 42 | x
4 | 42 | y
My goal is to count the data with X status except if the user has a another data with Y status, it will exclude in the count. So instead of 3, it will only count 2 since the 3rd row has another data which is the 4th row with y status.
SELECT * FROM logs
AND user_id NOT IN (SELECT user_id FROM logs WHERE status = 'y')
GROUP BY user_id;
We can try the following aggregation approach:
SELECT COUNT(*) AS cnt
FROM
(
SELECT user_id
FROM logs
GROUP BY user_id
HAVING MIN(status) = MAX(status) AND
MIN(status) = 'x'
) t;
The above logic only counts a user having one or more records only having x status.
You can do it this way, I only modify a bit on your sql
SELECT COUNT(*) FROM (
SELECT u_id FROM tbl WHERE u_id NOT IN
(SELECT u_id FROM tbl WHERE status = 'y')
GROUP BY u_id
) as t
You can use inner join:
SELECT
count(t1.id) AS `cnt`
FROM
`test` AS t1,
`test` AS t2
WHERE
t2.`status`='y'
&& t1.`user_id` != t2.`user_id`;

SQL counting occurrences of conditional statements using values from 2 different roles

I have a table with fields including time (UTC) and accountID.
accountID | time | ...
1 |12:00 |....
1 |12:01 |...
1 |13:00 |...
2 |14:00 |...
I need to make an sql query to return the accountID with a new field counting 'category' where 'category' can be 'a' or 'b'. If there is a row entry from the same accountID that has a positive time difference of 1 minute or less, category 'a' needs to be incremented, otherwise 'b'. The results from the above table would be
accountID| cat a count| cat b count
1 | 1 | 2
2 | 0 | 1
What approaches can I take to compare values between different rows and output occurrences of comparison outcomes?
Thanks
To compute this categories you'll need to pre-compute the findings of close rows in a "table expression". For example:
select
accountid,
sum(case when cnt > 0 then 1 else 0 end) as cat_a_count,
sum(case when cnt = 0 then 1 else 0 end) as cat_b_count
from (
select
accountid, tim,
( select count(*)
from t b
where b.accountid = t.accountid
and b.tim <> t.tim
and b.tim between t.tim and addtime(t.tim, '00:01:00')
) as cnt
from t
) x
group by accountid
Result:
accountid cat_a_count cat_b_count
--------- ----------- -----------
1 1 2
2 0 1
For reference, the data script I used is:
create table t (
accountid int,
tim time
);
insert into t (accountid, tim) values
(1, '12:00'),
(1, '12:01'),
(1, '13:00'),
(2, '14:00');
Use lag() and conditional aggregation:
select accountid,
sum(prev_time >= time - interval 1 minute) as a_count,
sum(prev_time < time - interval 1 minute or prev_time is null) as b_count
from (select t.*,
lag(time) over (partition by accountid order by time) as prev_time
from t
) t
group by accountid;

SQL statement for querying with multiple conditions including 3 most recent dates

I need help in finding the rows that correspond to the most recent date, the next most recent and the one after that, where some condition ABC is "Y" and group it by a column name XYZ ASC but XYZ can appear multiple times. So, say XYZ is 50, then for the rows in the three years, the XYZ will be 50. I have the following code that executes but returns only two rows out of thousands which is impossible. I tried executing just the date condition but it returned dates that were less than or equal to MAX(DATE)-3 as well. Don't know where I am going wrong.
select * from money.cash where DATE =(
select
MAX(DATE)
from
money.cash
where
DATE > (select MAX(DATE)-3 from money.cash)
)
GROUP BY XYZ ASC
having ABC = "Y";
The structure of the table is as follows (only a schematic, not the real thing).
Comp_ID DATE XYZ ABC $$$$ ....
1 2012-1-1 10 Y SOME-AMOUNT
2 2011-1-1 10 Y
3 2006-1-1 10 Y
4 2011-1-1 20 Y
5 2002-1-1 20 Y
6 2000-1-1 20 Y
7 1998-1-1 20 Y
The desired o/p would be the first three rows for XYZ=10 in ascending order and the most recent 3 dates for XYZ=20.
LAST AND IMPORTANT-This table's values keeps changing as new data comes in. So, the o/p(which will be in a new table) must reflect the dynamics in the 1st/original/above TABLE.
MySQL doesn't have functionallity that is friendly to greatest-n-per-group queries.
One option would be...
- Find the MAX(Date) per group (XYZ)
- Then use that result to find the MAX(Date) of all records before that date
- Then do it again for all records before that date
It's really innefficient, but MySQL hasn't got the functionality required to do this efficiently. Sorry...
CREATE TABLE yourTable
(
comp_id INT,
myDate DATE,
xyz INT,
abc VARCHAR(1)
)
;
INSERT INTO yourTable SELECT 1, '2012-01-01', 10, 'Y';
INSERT INTO yourTable SELECT 2, '2011-01-01', 10, 'Y';
INSERT INTO yourTable SELECT 3, '2006-01-01', 10, 'Y';
INSERT INTO yourTable SELECT 4, '2011-01-01', 20, 'Y';
INSERT INTO yourTable SELECT 5, '2002-01-01', 20, 'Y';
INSERT INTO yourTable SELECT 6, '2000-01-01', 20, 'Y';
INSERT INTO yourTable SELECT 7, '1998-01-01', 20, 'Y';
SELECT
yourTable.*
FROM
(
SELECT
lookup.XYZ,
COALESCE(MAX(yourTable.myDate), lookup.MaxDate) AS MaxDate
FROM
(
SELECT
lookup.XYZ,
COALESCE(MAX(yourTable.myDate), lookup.MaxDate) AS MaxDate
FROM
(
SELECT
yourTable.XYZ,
MAX(yourTable.myDate) AS MaxDate
FROM
yourTable
WHERE
yourTable.ABC = 'Y'
GROUP BY
yourTable.XYZ
)
AS lookup
LEFT JOIN
yourTable
ON yourTable.XYZ = lookup.XYZ
AND yourTable.myDate < lookup.MaxDate
AND yourTable.ABC = 'Y'
GROUP BY
lookup.XYZ,
lookup.MaxDate
)
AS lookup
LEFT JOIN
yourTable
ON yourTable.XYZ = lookup.XYZ
AND yourTable.myDate < lookup.MaxDate
AND yourTable.ABC = 'Y'
GROUP BY
lookup.XYZ,
lookup.MaxDate
)
AS lookup
INNER JOIN
yourTable
ON yourTable.XYZ = lookup.XYZ
AND yourTable.myDate >= lookup.MaxDate
WHERE
yourTable.ABC = 'Y'
ORDER BY
yourTable.comp_id
;
DROP TABLE yourTable;
There are other options, but they're all a bit hacky. Search SO for greatest-n-per-group mysql.
My results using your example data:
Comp_ID | DATE | XYZ | ABC
------------------------------
1 | 2012-1-1 | 10 | Y
2 | 2011-1-1 | 10 | Y
3 | 2006-1-1 | 10 | Y
4 | 2011-1-1 | 20 | Y
5 | 2002-1-1 | 20 | Y
6 | 2000-1-1 | 20 | Y
Here's another way, hopefully more efficient than Dems' answer.
Test it with an index on (abc, xyz, date):
SELECT m.xyz, m.date --- for all columns: SELECT m.*
FROM
( SELECT DISTINCT xyz
FROM money.cash
WHERE abc = 'Y'
) AS dm
JOIN
money.cash AS m
ON m.abc = 'Y'
AND m.xyz = dm.xyz
AND m.date >= COALESCE(
( SELECT im.date
FROM money.cash AS im
WHERE im.abc = 'Y'
AND im.xyz = dm.xyz
ORDER BY im.date DESC
LIMIT 1
OFFSET 2 --- to get 3 latest rows per xyz
), DATE('1000-01-01') ) ;
If you have more than rows with same (abc, xyz, date), the query may return more than 3 rows per xyz (all tied in 3rd place will all be shown).

Select Query to get last 3 calls and mails

I have 2 tables. One of tables has all mails from users and other table has all calls from users.
Table 1
call_id, | call_date | user_id
1 | 10/01/12| 3
2 | 9/01/12 | 3
Table 2
mail_id, | mail_date | user_id
1 | 8/01/12 | 3
2 7/01/12 | 3
I need to get last last 3 calls and mails :
10/01/12 - call
9/01/12 - call
8/01/12 - mail
Assuming MySQL, and for just one User_ID...
SELECT
*
FROM
(
SELECT 'call' AS type, id, call_date AS event_date, user_id FROM table_1
UNION ALL
SELECT 'mail' AS type, id, mail_date AS event_date, user_id FROM table_2
)
data
WHERE
user_id = 3
ORDER BY
event_date DESC
LIMIT
3
EDIT: Ooops, forgot to specify DESC in the ORDER BY, sorry.
Declare #userID int;
SET #userID=3;
select call_id FROM table_1
where user_id=#userID
order by call_date desc limit 2
UNION ALL
select mail_id FROM table_2
where user_id=#userID
order by mail_date desc limit 1

SQL get all rows sorted without duplicates

I have a table that looks something like this:
________________________
|id|value|date|approved|
-----------------------
What I need to be able to do is get each row where approved = 1. That part is obvious. For each occurrence of value, I only want the most recent row (sorted by date).
Meaning that with a table like this:
________________________
|id|value|date|approved|
-----------------------
|1 |Foo | 5 | 1 |
|2 |Bar | 6 | 1 |
|3 |Foo | 8 | 1 |
-----------------------
I only want the rows with id 2 and 3.
I assume I need to use DISTINCT somehow, but I'm not sure how. Could anyone help me out here?
SELECT m.*
FROM (
SELECT DISTINCT value
FROM mytable
) md
JOIN mytable m
ON m.id =
(
SELECT id
FROM mytable mi
WHERE mi.value = md.value
AND mi.approved = 1
ORDER BY
mi.value DESC, mi.date DESC, mi.id DESC
LIMIT 1
)
Create an index on (value, date, id) for this to work fast.
You need:
select id, value, date, approved where (value, date) in (
select value, max(date)
from your_table
group by value
);
Actually using GROUP BY will yeld better results. try something like this:
SELECT id, value, date, approved FROM table WHERE approved = 1 GROUP BY value ORDER BY date;
select id, value, date, approved
from mytable a
where approved = 1
and date =
(select max(b.date)
from mytable b
where b.approved = 1
and b.value = a.value)
select
id,
value,
date
from
( select
value, max( date ) as LastInstance
from
YourTable
where
approved = 1
group by
value ) PreQuery
join YourTable
on PreQuery.value = YourTable.value
and PreQuery.LastInstance = YourTable.LastInstance
and YourTable.approved = 1
order by
date
Try with this:
SELECT * DISTINCT FROM TABLA WHERE APPROVED = 1 ORDER BY DATE DESC
Hope this helps you.