Select a row from similar rows MYSQL - mysql

I have a Table like this
CREATE TABLE prova
(`ID` int, `CODCLI` longtext, `RIFYEAR` int, `VAL` int)
;
INSERT INTO prova
(`ID`, `CODCLI`, `RIFYEAR`, `VAL`)
VALUES
(1, '1dad000', 2020, 150),
(2, '500', 2020, 100),
(3, '1dad000', 2021, 50),
(4, '1dad000', 2022, 70),
(5, '2000', 2023, 80)
;
http://www.sqlfiddle.com/#!9/7697a4/4
and i want to select these rows
2, '500', 2020, 100
3, '1dad000', 2021, 50
4, '1dad000', 2022, 70
how can i do?
i wrote something like this
SELECT *
FROM prova
WHERE CODCLI IN ('500','1dad000')
but when 'RIFYEAR' is same, i want to select only the row that has CODCLI = 500.
thanks for your help ;)

One method uses window function:
SELECT p.*
FROM (SELECT p.*,
ROW_NUMBER() OVER (PARTITION BY year ORDER BY CODCLI DESC) as seqnum
FROM prova p
WHERE CODCLI IN ('500', '1dad000')
) p
WHERE seqnum = 1;
This is guaranteed to return one row per year.
Or using NOT EXISTS:
select p.*
from prova p
where p.codcli = '500' or
(p.codcli = '1dad000' and
not exists (select 1
from prova p2
where p2.year = p.year and p2.codcli = '500'
)
);
This can return duplicates per year if there are duplicates in the prova.

Related

How to calculate active users percentage in SQL

I have two tables;
db_user (user_id,create_date,country_code)
db_payment (user_id,pay_amount,pay_date)
I am trying to find what percentage of users are active in 2021 Feb, among all the users joined in 2021 Jan.
I can separately find the total users created in Jan 2021 and who is active in Feb 2021 (see my below query). Then simply divide the numbers. However, I am trying to find all in one query.
SELECT
count(distinct u.user_id) as active_users_jan_to_feb
from db_user as u
left join db_payment as p
ON u.user_id = p.user_id
where YEAR(STR_TO_DATE(u.create_date, "%Y-%m-%d"))=2021
and MONTH (STR_TO_DATE(u.create_date, "%Y-%m-%d"))=01
and YEAR(STR_TO_DATE(p.pay_date, "%Y-%m-%d"))=2021
and MONTH(STR_TO_DATE(p.pay_date, "%Y-%m-%d"))=02
SELECT
count(distinct u.user_id) as total_users_jan_2021
from db_user as u
where YEAR(STR_TO_DATE(u.create_date, "%Y-%m-%d"))=2021
and MONTH (STR_TO_DATE(u.create_date, "%Y-%m-%d"))=01
I joined two tables to create a master view of what users created in what year/month and their payment year/month. However, I am not sure how to go from this master view to find the percentage of users are created in 2021 Jan and they are active in 2021 Feb. Can you please help me understand how I should approach it?
SELECT
u.user_id
,YEAR(STR_TO_DATE(u.create_date, "%Y-%m-%d")) as create_year
,MONTH (STR_TO_DATE(u.create_date, "%Y-%m-%d")) as create_month
,YEAR(STR_TO_DATE(p.pay_date, "%Y-%m-%d")) as pay_year
,MONTH(STR_TO_DATE(p.pay_date, "%Y-%m-%d")) as pay_month
,p.payment_amount
from db_user as u
left join db_payment as p
ON u.user_id = p.user_id
Here is the table creation and sample data import;
CREATE TABLE db_user
(
user_id int PRIMARY KEY,
create_date TEXT,
country_code TEXT
);
INSERT INTO db_user (user_id, create_date, country_code)
VALUES
(1, '2019-01-01', 'US'),
(2, '2020-02-01', 'US'),
(3, '2021-01-01', 'US'),
(4, '2021-02-01', 'TR'),
(5, '2021-03-01', 'FR'),
(6, '2021-06-01', 'FR'),
(7, '2021-02-11', 'US'),
(8, '2021-02-19', 'TR'),
(9, '2021-01-10', 'US');
CREATE TABLE db_payment
(
user_id int,
payment_amount double,
pay_date TEXT
);
INSERT INTO db_payment (user_id, payment_amount, pay_date)
VALUES
(1, 10, '2019-01-01'),
(1, 10, '2019-02-01'),
(1, 10, '2019-03-01'),
(3, 10, '2021-01-01'),
(3, 10, '2021-02-01'),
(4, 10, '2021-02-01'),
(4, 10, '2021-03-01');
SELECT 100 * COUNT(DISTINCT db_payment.user_id) / COUNT(DISTINCT db_user.user_id) AS percent_active_in_Feb_from_joined_in_Jan
FROM db_user
LEFT JOIN db_payment ON db_payment.user_id = db_user.user_id
AND db_payment.pay_date BETWEEN '2021-02-01' AND '2021-02-28'
WHERE db_user.create_date BETWEEN '2021-01-01' AND '2021-01-31';
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=bb1ac14b5410f65b2922a9f771a8c6db

MYSQL 5.6 get latest data of each user

My Database table is as shown below. I need to get latest mark of each student. Latest entry is the row with maximum udate and maximum oder. (The oder will be incremented by one on each entry with same date)
In my example, I have two students Mujeeb, Zakariya and two subjects ENGLISH, MATHS. I need to get latest mark of each student for each subject. My expectd result is as follows
My sample data is
DROP TABLE IF EXISTS `students`;
CREATE TABLE IF NOT EXISTS `students` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`udate` date NOT NULL,
`oder` int(11) NOT NULL,
`name` varchar(20) NOT NULL,
`Subject` varchar(20) NOT NULL,
`mark` int(11) NOT NULL,
PRIMARY KEY (`uid`)
) ENGINE=MyISAM AUTO_INCREMENT=13 DEFAULT CHARSET=latin1;
INSERT INTO `students` (`uid`, `udate`, `oder`, `name`, `Subject`, `mark`) VALUES
(1, '2021-08-01', 1, 'Mujeeb', 'ENGLISH', 10),
(2, '2021-08-01', 1, 'Zakariya', 'ENGLISH', 20),
(3, '2021-08-10', 2, 'Mujeeb', 'ENGLISH', 50),
(4, '2021-08-11', 2, 'Zakariya', 'ENGLISH', 60),
(5, '2021-08-02', 1, 'Mujeeb', 'ENGLISH', 100),
(6, '2021-08-03', 1, 'Zakariya', 'ENGLISH', 110),
(7, '2021-08-10', 1, 'Mujeeb', 'ENGLISH', 500),
(8, '2021-08-11', 1, 'Zakariya', 'ENGLISH', 600),
(9, '2021-08-01', 2, 'Mujeeb', 'MATHS', 100),
(10, '2021-08-01', 2, 'Zakariya', 'MATHS', 75),
(11, '2021-08-10', 3, 'Mujeeb', 'MATHS', 50),
(12, '2021-08-11', 3, 'Zakariya', 'MATHS', 60);
Use NOT EXISTS:
SELECT s1.*
FROM students s1
WHERE NOT EXISTS (
SELECT 1
FROM students s2
WHERE s2.name = s1.name AND s2.Subject = s1.Subject
AND (s2.udate > s1.udate OR (s2.udate = s1.udate AND s2.oder > s1.oder))
);
Or with a correlated subquery in the WHERE clause:
SELECT s1.*
FROM students s1
WHERE s1.uid = (
SELECT s2.uid
FROM students s2
WHERE s2.name = s1.name AND s2.Subject = s1.Subject
ORDER BY s2.udate DESC, s2.oder DESC LIMIT 1
);
See the demo.
As ROW_NUMBER() function doesn't work at lower version of MySQL, So alternate way of row_number() is used for this solution.
-- MySQL (v5.6)
SELECT p.uid, p.udate, p.oder, p.name, p.Subject, p.mark
FROM (SELECT #row_no := IF((#prev_val = t.name && #prev_val1 = t.Subject), #row_no + 1, 1) AS row_number
, #prev_val := t.name AS name
, #prev_val1 := t.Subject AS Subject
, t.mark
, t.oder
, t.uid
, t.udate
FROM students t,
(SELECT #row_no := 0) x,
(SELECT #prev_val := '') y,
(SELECT #prev_val1 := '') z
ORDER BY t.name, t.Subject, t.udate DESC, t.oder DESC ) p
WHERE p.row_number = 1
ORDER BY p.name, p.Subject;
Please check the url http://sqlfiddle.com/#!9/b5befe/18

Get previous X days of revenue for each group

Here is my table
CREATE TABLE financials (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
CountryID VARCHAR(30) NOT NULL,
ProductID VARCHAR(30) NOT NULL,
Revenue INT NOT NULL,
cost INT NOT NULL,
reg_date TIMESTAMP
);
INSERT INTO `financials` (`id`, `CountryID`, `ProductID`, `Revenue`, `cost`, `reg_date`) VALUES
( 1, 'Canada', 'Doe' , 20, 5, '2010-01-31 12:01:01'),
( 2, 'USA' , 'Tyson' , 40, 15, '2010-02-14 12:01:01'),
( 3, 'France', 'Keaton', 80, 25, '2010-03-25 12:01:01'),
( 4, 'France', 'Keaton',180, 45, '2010-04-24 12:01:01'),
( 5, 'France', 'Keaton', 30, 6, '2010-04-25 12:01:01'),
( 6, 'France', 'Emma' , 15, 2, '2010-01-24 12:01:01'),
( 7, 'France', 'Emma' , 60, 36, '2010-01-25 12:01:01'),
( 8, 'France', 'Lammy' ,130, 26, '2010-04-25 12:01:01'),
( 9, 'France', 'Louis' ,350, 12, '2010-04-25 12:01:01'),
(10, 'France', 'Dennis',100,200, '2010-04-25 12:01:01'),
(11, 'USA' , 'Zooey' , 70, 16, '2010-04-25 12:01:01'),
(12, 'France', 'Alex' , 2, 16, '2010-04-25 12:01:01');
For each product and date combination, I need to get the revenue for previous 5 days. For instance, for Product ‘Keaton’, the last purchase was on 2010-04-25, it will only sum up revenue between 2010-04-20 to 2010-04-25 and therefore it will be 210. While for "Emma", it would return 75, since it would sum everything between 2010-01-20 to 2010-01-25.
SELECT ProductID, sum(revenue), reg_date
FROM financials f
Where reg_date in (
SELECT reg_date
FROM financials as t2
WHERE t2.ProductID = f.productID
ORDER BY reg_date
LIMIT 5)
Unfortunately, when i use either https://sqltest.net/ or http://sqlfiddle.com/ it says that 'LIMIT & IN/ALL/ANY/SOME subquery' is not supported. Would my query work or not?
Your query is on the right track, but probably won't work in MySQL. MySQL has limitations on the use of in and limit with subqueries.
Instead:
SELECT f.ProductID, SUM(f.revenue)
FROM financials f JOIN
(SELECT ProductId, MAX(reg_date) as max_reg_date
FROM financials
GROUP BY ProductId
) ff
ON f.ProductId = ff.ProductId and
f.reg_date >= ff.max_reg_date - interval 5 day
GROUP BY f.ProductId;
EDIT:
If you want this for each product and date combination, then you can use a self join or correlated subquery:
SELECT f.*,
(SELECT SUM(f2.revenue)
FROM financials f2
WHERE f2.ProductId = f.ProductId AND
f2.reg_date <= f.reg_date AND
f2.reg_date >= f.reg_date - interval 5 day
) as sum_five_preceding_days
FROM financials f;
After some trials I ended up with some complex query, that I think it solves your problem
SELECT
financials.ProductID, sum(financials.Revenue) as Revenues
FROM
financials
INNER JOIN (
SELECT ProductId, GROUP_CONCAT(id ORDER BY reg_date DESC) groupedIds
FROM financials
group by ProductId
) group_max
ON financials.ProductId = group_max.ProductId
AND FIND_IN_SET(financials.id, groupedIds) BETWEEN 1 AND 5
group by financials.ProductID
First I used group by financials.ProductID to count revenues by products. The real problem you are facing is eliminating all rows that are not in the top 5, for each group. For that I used the solution from this question, GROUP_CONCAT and FIND_IN_SET, to get the top 5 result without LIMIT. Instead of WHERE IN I used JOIN but with this, WHERE IN might also work.
Heres the FIDDLE

Selecting only the first two items from and order

I need you help regarding something, i have 3 tables ORDERS, ORDER_ITEM, ORDER_ITEM_LINE.
CREATE TABLE orders
(`id` int, `date` datetime)
;
INSERT INTO orders
(`id`, `date`)
VALUES
(78, '2017-01-03 00:00:00'),
(79, '2017-02-03 00:00:00'),
(80, '2017-03-03 00:00:00'),
(81, '2017-04-03 00:00:00'),
(82, '2017-05-03 00:00:00'),
(83, '2017-06-03 00:00:00'),
(84, '2017-07-03 00:00:00')
;
CREATE TABLE order_item
(`id` int, `fk_o_id` int, `sku` int)
;
INSERT INTO order_item
(`id`, `fk_o_id`, `sku`)
VALUES
(10, 78, 123),
(11, 79, 124),
(12, 79, 125),
(13, 80, 126),
(14, 82, 127),
(15, 82, 128),
(16, 82, 129)
;
CREATE TABLE order_item_line
(`id` int, `fk_oi_id` int, `line_id` int)
;
INSERT INTO order_item_line
(`id`, `fk_oi_id`, `line_id`)
VALUES
(33, 10, 1),
(34, 11, 1),
(35, 12, 2),
(36, 13, 1),
(37, 14, 1),
(38, 15, 2),
(39, 16, 3)
;
I would like to display all orders with 2 or more than 2 items but only first two so it will be line_id - 1 and 2.
The outcome should look like:
Outcome
If you have any ideas, thank you in advance.
To get the result you require, you will need to create another table. In this example I created a table called TESTQUERY and inserted data to count how many times the orders id appeared
Table creation
CREATE TABLE TESTQUERY
(`id` int, `count` int)
Data into the test table
INSERT INTO TESTQUERY
(
SELECT o.id, COUNT(o.id) as count FROM orders o
JOIN order_item oi ON oi.fk_o_id = o.id
JOIN order_item_line oil ON oil.fk_oi_id = oi.id
GROUP BY o.id
)
I then queried against all for databases using the query below and it returned your desired outcome
SELECT o.id, oi.sku, oil.line_id FROM orders o
JOIN order_item oi ON oi.fk_o_id = o.id
JOIN order_item_line oil ON oil.fk_oi_id = oi.id
JOIN TESTQUERY t ON t.id = o.id
WHERE t.count > 1 AND oil.line_id < 3
I hope this helps

How to get combined column values from multiple tables with different columns

I am trying to combine multiple columns from three tables. I could do it using UNION ALL keyword but I am feeling this query what I use is not probably the most efficient
For example:
create table tbl1
(id int, act varchar(50), stk varchar(50), price int, vol int, amt float);
insert into tbl1 values
(1, 'a1', 's1', 10, 5, 50),
(2, 'a1', 's2', 5, 5, 25),
(3, 'a2', 's1', 15, 3, 45),
(4, 'a2', 's2', 20, 2, 40),
(5, 'a2', 's2', 20, 2, 40);
create table tbl2 (id int, tid int, price int, vol int, amt float);
insert into tbl2 values
(1, 1, 5, 3, 15),(2, 1, 5, 1, 5),(3, 1, 15, 1, 15),
(4, 2, 5, 3, 15),(5, 2, 6, 2, 12);
create table tbl3 (id int, act varchar(10), type int, amt float);
insert into tbl3 values
(1, 'a1', 0, 10),(2, 'a1', 1, 15),
(3, 'a2',1, 5),(4, 'a3',0, 5);`
The query I used
SELECT act,stk,amtFROM tbl1
UNION ALL
SELECT
(select act from tbl1 where tbl2.tid = tbl1.id) amt,
(select stk from tbl1 where tbl2.tid = tbl1.id) stk,
amt
from tbl2
Is there a way to get the same without using inner select queries twice? could someone please give me the efficient query?
here is the Fiddle
Expected output (amt from all three tables where act='a1')
ACT STK AMT
a1 s1 50
a1 s2 25
a1 s1 15
a1 s1 5
a1 s1 15
a1 s1 10
a1 s1 15
Just use an explicit join:
SELECT act, stk, amt
FROM tbl1
UNION ALL
SELECT t1.act as amt, t1.stk, t2.amt
from tbl2 join
tbl1
on tbl2.tid = tbl1.id;