MySQL-Count consective number - mysql

Write a SQL query to find number position as well number and consective number count
CREATE TABLE Logs (
`Id` INTEGER,
`Num` INTEGER
);
INSERT INTO Logs
(`Id`, `Num`)
VALUES
('1', '1'),
('2', '1'),
('3', '1'),
('4', '2'),
('5', '1'),
('6', '2'),
('7', '2');
Prefere Return
StartId Num Count
1 1 3
4 2 1
5 1 1
6 2 2
and also can i get any suggestion which function can be use with case function in MySQL Function

Looking at your data and expected results, I believe your expectations are inconsistent, eg you can either have 1 and 6 or 3 and 7.
What you need to do is group the data by successive num values and aggregate the results.
with gp as (
select *,
Row_Number() over(order by id)
- Row_Number() over(partition by num order by id) g
from logs
)
select Min(id) Id,
num, Count(*) Count
from gp
group by g, num
order by id

Related

Update a column with count of the values from another column

I am creating a MySQL table with the following query.
CREATE TABLE log_table (id INTEGER, street TEXT, status INTEGER)
and filling it with the following query:
INSERT INTO log_table (`id`, `street`, `status`) VALUES
('1', 'main_street', '0'),
('2', '1st_street', '0'),
('3', '1st_street', '0'),
('4', 'main_street', '0'),
('5', '2nd_street', '0'),
('6', '1st_street', '0'),
('7', 'main_street', '0'),
('8', '2nd_street', '0');
I am trying to update the status column with the count of the street column.
For example for the first row status column should be 3 because main_street appears 3 times in street column.
I have tried the following query but it doesn't work.
UPDATE log_table l1 SET status =
(SELECT COUNT(*) FROM log_table l2 WHERE l2.street = l1.street);
It gives an error that says "You can't specify target table 'l1' for update in FROM clause". What could be the issue?
You can use a join:
UPDATE log_table l1
join (SELECT COUNT(*) as n, street FROM log_table l2 group by l2.street) as l3 on l1.street=l3.street
SET status = l3.n
SQLFiddle

Query for any person has any account of type x

Imagine I have two tables, Person and Account, a person can have accounts (type 1 and/or 2).
I'd like to get a list of people who have at least one type 1 account, and also get a list of people who don't have a type 1 account. I'm using Query #1 and #2 for this respectively but I think I'm doing something is wrong because the results do not match.
Schema (MySQL v5.7)
CREATE TABLE Person (
`PersonId` INTEGER,
`Name` VARCHAR(5)
);
INSERT INTO Person
(`PersonId`, `Name`)
VALUES
('1', 'Leo'),
('2', 'Natan'),
('3', 'Vera'),
('4', 'Julio'),
('5', 'Mary');
CREATE TABLE Accounts (
`AccountId` INTEGER,
`PersonId` INTEGER,
`Type` INTEGER
);
INSERT INTO Accounts
(`AccountId`, `PersonId`, `Type`)
VALUES
('1', '1', '0'),
('2', '1', '1'),
('3', '2', '0'),
('4', '2', '0'),
('5', '3', '1'),
('6', '4', '0'),
('7', '1', '0'),
('8', '2', '0');
Query #1
SELECT * FROM Person AS PD
LEFT JOIN Accounts AS AC ON AC.PersonId = PD.PersonId
WHERE AC.Type = 1;
PersonId
Name
AccountId
PersonId
Type
1
Leo
2
1
1
3
Vera
5
3
1
Query #2
SELECT * FROM Person AS PD
LEFT JOIN Accounts AS AC ON AC.PersonId = PD.PersonId
WHERE AC.Type = 0;
PersonId
Name
AccountId
PersonId
Type
1
Leo
1
1
0
1
Leo
7
1
0
2
Natan
3
2
0
2
Natan
4
2
0
2
Natan
8
2
0
4
Julio
6
4
0
View on DB Fiddle
EXISTS and NOT EXISTS are the more suitable solutions for this requirement:
-- Account type = 1
SELECT p.* FROM Person AS p
WHERE EXISTS (
SELECT *
FROM Accounts AS a
WHERE a.PersonId = p.PersonId AND a.Type = 1
);
-- No type 1 account
SELECT p.* FROM Person AS p
WHERE NOT EXISTS (
SELECT *
FROM Accounts AS a
WHERE a.PersonId = p.PersonId AND a.Type = 1
);
See the demo.

Select highest value based on aggregate function - SQL

I have a GiftSales table, it contains the id of the item (giftId), and the category of that item (categoryId)
I need to get the best selling item for each category.
Right now my query looks like this
SELECT giftId, categoryId, COUNT(giftId) as Total
FROM GiftSales
GROUP BY giftId, categoryId
And its giving me
==================================
|| giftId || categoryId || Total||
==================================
|| 1 || 1 || 8 ||
==================================
|| 2 || 1 || 5 ||
==================================
|| 23 || 2 || 12 ||
==================================
I need to only show the highest value per each category, so basically, the table shouldn't contain the second item.
I'd recommend using a window function, and dense_rank can be helpful when looking at top selling products by category as you may want to include any ties.
Schema (MySQL v8.0)
CREATE TABLE IDs (
`gift_id` INTEGER,
`category_id` INTEGER
);
INSERT INTO IDs
(`gift_id`, `category_id`)
VALUES
('1', '1'),
('1', '1'),
('1', '1'),
('1', '1'),
('1', '1'),
('1', '1'),
('1', '1'),
('1', '1'),
('2', '1'),
('2', '1'),
('2', '1'),
('2', '1'),
('2', '1');
Query #1
select a.category_id,a.gift_id,a.total from (
select
category_id,
gift_id,
count(gift_id) as total,
dense_rank() over (partition by category_id order by count(gift_id) desc) as ranking
from IDs group by 1,2) as a where ranking = 1;
category_id
gift_id
total
1
1
8
View on DB Fiddle
Use a window function such as MAX OVER per category:
select giftid, categoryid, total
from
(
select
giftid,
categoryid,
count(*) as total,
max(count(*)) over (partition by categoryid) as category_max
from giftsales
group by giftid, categoryid
) aggregated
where total = category_max;
SELECT DISTINCT categoryId, MAX(Total) as total FROM(
SELECT giftId, categoryId, COUNT(giftId) as Total FROM GiftSales GROUP BY giftId, categoryId
) AS T GROUP BY giftId, categoryId;
I got it working by using the distinct with the categoryId and since you need the total by category I removed the giftId and everything worked fine :) I used a playground to test this and the playground can be found here -> https://www.db-fiddle.com/f/qsGLKUZyos2ZKTftykkazd/0

How to get difference or delta of counts entries of each days with window functions?

I have a table with few fields like id, country, ip, created_at. Then I am trying to get the deltas between total entry of one day and total entry of the next day.
CREATE TABLE session (
id int NOT NULL AUTO_INCREMENT,
country varchar(50) NOT NULL,
ip varchar(255),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);
INSERT INTO `session` (`id`, `country`, `ip`, `created_at`) VALUES
('1', 'IN', '10.100.102.11', '2021-04-05 20:26:02'),
('2', 'IN', '10.100.102.11', '2021-04-05 19:26:02'),
('3', 'US', '10.120.102.11', '2021-04-17 10:26:02'),
('4', 'US', '10.100.112.11', '2021-04-16 12:26:02'),
('5', 'AU', '10.100.102.122', '2021-04-12 19:36:02'),
('6', 'AU', '10.100.102.122', '2021-04-12 18:20:02'),
('7', 'AU', '10.100.102.122', '2021-04-12 23:26:02'),
('8', 'US', '10.100.102.2', '2021-04-16 21:33:01'),
('9', 'AU', '10.100.102.122', '2021-04-18 20:46:02'),
('10', 'AU', '10.100.102.111', '2021-04-04 13:19:12'),
('11', 'US', '10.100.112.11', '2021-04-16 12:26:02'),
('12', 'IN', '10.100.102.11', '2021-04-05 15:26:02'),
('13', 'IN', '10.100.102.11', '2021-04-05 19:26:02');
Now I have written this query to get the delta
SELECT T1.date1 as date, IFNULL(T1.cnt1-T2.cnt2, T1.cnt1) as delta from (
select TA.dateA as date1, MAX(TA.countA) as cnt1 from (
select DATE(created_at) AS dateA, COUNT(*) AS countA
FROM session
GROUP BY DATE(created_at)
UNION
select DISTINCT DATE(DATE(created_at)+1) AS dateA, 0 AS countA
FROM session
) as TA
group by TA.dateA
) as T1
LEFT OUTER JOIN (
select DATE(DATE(created_at)+1) AS date2,
COUNT(*) AS cnt2
FROM session
GROUP BY DATE(created_at)
) as T2
ON T1.date1=T2.date2
ORDER BY date;
http://sqlfiddle.com/#!9/4f5fd26/60
Then I am getting the results as
date delta
2021-04-04 1
2021-04-05 3
2021-04-06 -4
2021-04-12 3
2021-04-13 -3
2021-04-16 3
2021-04-17 -2
2021-04-18 0
2021-04-19 -1
Now, is there any place of improvements/optimizes on it with/or window functions? (I am zero with SQL, still playing around).
Try a shorter version
with grp as (
SELECT t.dateA, SUM(t.cnt) AS countA
FROM session,
LATERAL (
select DATE(created_at) AS dateA, 1 as cnt
union all
select DATE(DATE(created_at)+1), 0 as cnt
) t
GROUP BY dateA
)
select t1.dateA as date, IFNULL(t1.countA-t2.countA, t1.countA) as delta
from grp t1
left join grp t2 on DATE(t2.dateA + 1) = t1.dateA
order by t1.dateA
db<>fiddle

Ties on Hall of Fame (group player, max level then max score for each game when month is...)

Need to list a Hall of Fame of best players, the database contains each single game player in different games.
The level has the priority, if the level are the same, check the highest score.
I've a database with user_id, level, score, game and data. Schema here:
CREATE TABLE IF NOT EXISTS `docs` (`user_id` int(6) unsigned NOT NULL,
`level` int(3) unsigned NOT NULL,`game` varchar(30) NOT NULL,
`score` int(5) unsigned NOT NULL,
`data` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `docs` (`user_id`, `level`, `game`, `score`,`data`) VALUES
('1', '7', 'pacman', '8452','2018-02-14 15:00:00'),
('1', '9', 'pacman', '9999','2018-02-10 16:30:00'),
('2', '8', 'pacman', '8500','2018-02-24 17:30:00'),
('1', '10', 'pacman', '9100','2018-02-15 18:30:00'),
('1', '10', 'pacman', '8800','2018-02-15 18:11:00'),
('1', '11', 'snake', '9600','2018-02-14 15:00:00'),
('1', '6', 'snake', '7020','2018-02-11 11:30:00'),
('2', '8', 'snake', '8500','2018-02-24 14:00:00'),
('2', '12', 'snake', '9200','2018-02-25 19:00:00'),
('2', '12', 'snake', '9800','2018-02-25 19:20:00'),
('1', '4', 'pacman', '2452','2018-03-11 15:00:00'),
('1', '6', 'pacman', '4999','2018-03-07 16:30:00'),
('2', '7', 'pacman', '5500','2018-03-02 17:30:00'),
('1', '7', 'pacman', '5100','2018-03-01 18:30:00'),
('1', '3', 'snake', '3600','2018-03-03 15:00:00'),
('1', '5', 'snake', '4220','2018-03-01 11:30:00'),
('2', '5', 'snake', '3900','2018-03-04 14:00:00'),
('2', '5', 'snake', '5200','2018-03-05 19:00:00');
i want retrieve the hall of fame for selected month and game,
for example if i choose pacman on march the result should be:
user level score
2 7 5500
1 7 5100
i tryed this how suggest in other similar topic
select d1.*
from docs d1
left outer join docs d2
on (d1.user_id = d2.user_id and d1.level < d2.level)
where d2.user_id is null
order by level desc;
but i've duplicate levels for same user, then i cant choose the game or the month.
here there is the SQL Fiddle
SELECT x.* FROM docs x
JOIN
(select user_id
, game
, MONTH(data) month
, MAX(score) score
from docs
where game = 'pacman'
and MONTH(data) = 3
group
by user_id
, game
, MONTH(data)
) y
ON y.user_id = x.user_id
AND y.game = x.game
AND y.month = MONTH(x.data)
AND y.score = x.score;
or something like that
after a long work, study and research this is the best solution for me:
SELECT user_id, level, score, game
FROM (
SELECT *,
#rn := IF(user_id = #g, #rn + 1, 1) rn,
#g := user_id
FROM (select #g := null, #rn := 0) x,
docs where game='pacman'
ORDER BY user_id, level desc, score desc, game
) X
WHERE rn = 1 order by level desc, score desc;
the explanation is in this topic Select one value from a group based on order from other columns