MySql : Fetch records from multiple table by arithmetic operations - mysql

I've the following tables :
table_1
------------------
id uid category balance
1 1 A 100
2 2 B 80
table_2
------------------
id uid name
1 1 ABC
2 2 XYZ
table_2
------------------
id uid last_pay
1 1 10
2 1 10
3 1 10
4 2 80
I want to grab records from the three tables and the conditions are as follows :
a) table_1.category = something
b) ( table_1.balance - table_3.SUM(`last_pay`) ) > 0
I want the table_1.category = 'A' because (100 - (10 + 10 + 10)) > 0
Trying this query but its not working :
SELECT t1.uid,t1.category,t1.balance,t2.uid,t2.name FROM table_1 as t1
LEFT JOIN table_2 as t2 ON t1.uid = t2.uid
WHERE t1.category = 2
AND t1.balance - (SELECT SUM(`last_pay`) FROM table_3 WHERE uid = ? )

OK, I give you a full answer with normalized tables:
You can normalize the table_2 and insert the column name to table table_1. See the following and new table structure (with table_1 and table_2):
table_1
--------------------------------------------
id uid category balance name
1 1 A 100 ABC
2 2 B 80 XYZ
table_2
------------------------
id uid last_pay
1 1 10
2 1 10
3 1 10
4 2 80
To create these tables, you can use the following script:
CREATE TABLE table_1 (
`id` INT,
`uid` INT,
`category` VARCHAR(1),
`balance` INT,
`name` VARCHAR(3)
);
INSERT INTO table_1 VALUES
(1, 1, 'A', 100, 'ABC'),
(2, 2, 'B', 80, 'XYZ');
CREATE TABLE table_2 (
`id` INT,
`uid` INT,
`last_pay` INT
);
INSERT INTO table_2 VALUES
(1, 1, 10),
(2, 1, 10),
(3, 1, 10),
(4, 2, 80);
A query to get your expected result:
SELECT t1.uid, t1.category, t1.balance, t2.uid, t1.name
FROM table_1 t1
LEFT JOIN (
SELECT uid, SUM(last_pay) AS last_pay
FROM table_2
GROUP BY uid
) t2 ON t1.uid = t2.uid
WHERE (t1.balance - t2.last_pay) > 0
You can find a working example here: http://sqlfiddle.com/#!9/a2e27/3/0
You want to use your original tables? [answer for original question]
If it is possbile I recommend to normalize the tables! If it is not possible to change the table structure, you can use the following query to get your expected result:
SELECT t1.uid, t1.category, t1.balance, t2.uid, t2.name
FROM table_1 AS t1 LEFT JOIN table_2 AS t2 ON t1.uid = t2.uid
LEFT JOIN (
SELECT uid, SUM(last_pay) AS last_pay
FROM table_3
GROUP BY uid
) t3 ON t1.uid = t3.uid
WHERE (t1.balance - t3.last_pay) > 0
You can find a working example here: http://sqlfiddle.com/#!9/f22024/7/0

Create table/ insert data.
CREATE TABLE table_1
(`id` int, `uid` int, `category` varchar(1), `balance` int)
;
INSERT INTO table_1
(`id`, `uid`, `category`, `balance`)
VALUES
(1, 1, 'A', 100),
(2, 2, 'B', 80)
;
CREATE TABLE table_2
(`id` int, `uid` int, `name` varchar(3))
;
INSERT INTO table_2
(`id`, `uid`, `name`)
VALUES
(1, 1, 'ABC'),
(2, 2, 'XYZ')
;
CREATE TABLE table_3
(`id` int, `uid` int, `last_pay` int)
;
INSERT INTO table_3
(`id`, `uid`, `last_pay`)
VALUES
(1, 1, 10),
(2, 1, 10),
(3, 1, 10),
(4, 2, 80)
;
This query checks for both table_1 categories A and B
And If the table_1.balans is higher then 0 when table_3.last_pay is summed together per category
Query
SELECT
table_1.uid
, table_1.category
, table_1.balance
, table_2.uid
, table_2.name
FROM (
SELECT
uid
, SUM(table_3.last_pay) sum_last_pay
FROM
table_3
GROUP BY
table_3.uid
) AS
table_3_summed
INNER JOIN
table_1
ON
table_1.uid = table_3_summed.uid
INNER JOIN
table_2
ON
table_1.uid = table_2.uid
WHERE
(table_1.balance - table_3_summed.sum_last_pay) > 0
Result
uid category balance uid name
------ -------- ------- ------ --------
1 A 100 1 ABC

Related

Can you get a different column from a row with a MIN or MAX value?

I'm building an application with millions of rows, so I'm trying to avoid JOIN whenever possible. I have a table like this:
ID category value_1 value_2
1 1 2.2432 5.4321
2 2 6.5423 5.1203
3 1 8.8324 7.4938
4 2 0.4823 9.8244
5 2 7.2456 3.1278
6 1 1.9348 4.4421
I'm trying to retrieve value_1 from the row with the lowest ID and value_2 from the row with the highest ID while grouped by category, like this:
category value_1 value_2
1 2.2432 4.4421
2 6.5423 3.1278
Is this possible in an effective way while avoiding constructs like string operations and JOIN?
Thank you!
Try this:
SELECT
category,
(
SELECT t2.value1
FROM table1 t2
WHERE t2.id = MIN(t1.id)
) as value1,
(
SELECT t3.value2
FROM table1 t3
WHERE t3.id = MAX(t1.id)
) as value2
FROM
table1 t1
GROUP BY
category
;
Create and fill table:
CREATE TABLE `table1` (
`id` INT NOT NULL,
`category` INT NULL,
`value1` DOUBLE NULL,
`value2` DOUBLE NULL,
PRIMARY KEY (`id`)
);
INSERT INTO table1 VALUES
(1, 1, 2.2432, 5.4321),
(2, 2, 6.5423, 5.1203),
(3, 1, 8.8324, 7.4938),
(4, 2, 0.4823, 9.8244),
(5, 2, 7.2456, 3.1278),
(6, 1, 1.9348, 4.4421);
Output:
1 2.2432 4.4421
2 6.5423 3.1278
One approach which avoids joins is to use ROW_NUMBER:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY category ORDER BY ID) rn_min,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY ID DESC) rn_max
FROM yourTable
)
SELECT
category,
MAX(CASE WHEN rn_min = 1 THEN value_1 END) AS value_1,
MAX(CASE WHEN rn_max = 1 THEN value_2 END) AS value_2
FROM cte
GROUP BY
category;
Demo
Edit:
The above query should benefit from the following index:
CREATE INDEX idx ON yourTable (category, ID);
This should substantially speed up the row number operations.

MySQL: ORDER BY and GROUP BY together

I recently upgraded to MySQL 5.7.22 and my query stopped working. I have two tables "items" and "packages" where I'm trying to output a row for each item including a column for the package with the minimum price per unit, but ignore packages that have a price per unit set to 0.
Here's a minimal sample of tables and data:
CREATE TABLE `items` (
`id` int(11) NOT NULL
);
CREATE TABLE `packages` (
`item_id` int(11) NOT NULL,
`price_per_unit` float(16,6) DEFAULT 0
);
INSERT INTO `items` (`id`) VALUES
(1),
(2),
(3);
INSERT INTO `packages` (`item_id`, `price_per_unit`) VALUES
(1, 0.45),
(1, 0),
(1, 0.56),
(1, 0.34);
Here's the query:
SELECT
*
FROM
(
SELECT
items.id,
NULLIF(pkgs.ppu, 0) AS mppu
FROM
items
LEFT JOIN
(
SELECT
item_id,
price_per_unit AS ppu
FROM
packages
) AS pkgs ON pkgs.item_id = items.id
ORDER BY
IFNULL(mppu, 9999)
) X
GROUP BY
X.id
I was setting the zero values to null and then bumping their values to be much higher during the ordering. There must be a better way (especially since this method doesn't work any longer).
The expected output for this data is:
id mppu
1 0.34
2 null
3 null
I think your query is a bit too complex. What about this?
SELECT i.id,IFNULL(Min(p.price_per_unit), 'NONE')
FROM items i
LEFT JOIN packages p
ON ( i.id = p.item_id )
WHERE p.price_per_unit > 0
OR p.price_per_unit IS NULL
GROUP BY i.id
See this fiddle. I used this data:
INSERT INTO `items` (`id`) VALUES
(1),(2),(3);
INSERT INTO `packages` (`item_id`, `price_per_unit`) VALUES
(1, 0.45),
(1, 0),
(1, 0.56),
(1, 0.34),
(2, 9.45),
(2, 0),
(2, 0.56),
(2, 0.14);
And got this result:
id IFNULL(min(p.price_per_unit),'None')
1 0.340000
2 0.140000
3 None
Agree with GL,
SELECT * FROM GROUP BY
is not predictable .
i will rewrite the query with :
SELECT a.*,b.min_price_per_unit
FROM items a
LEFT JOIN (
SELECT item_id
,min(CASE
WHEN price_per_unit = 0
THEN 9999
ELSE price_per_unit
END) AS min_price_per_unit
FROM packages
GROUP BY item_id
) b ON a.id = b.item_id;

get rows from a table where value of field x is maximum

I have two tables myTable and myTable2 in a mysql database:
CREATE TABLE myTable (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
number INT,
version INT,
date DATE
) ENGINE MyISAM;
INSERT INTO myTable
(`id`, `number`, `version`, `date`)
VALUES
(1, '123', '1', '2016-01-12'),
(2, '123', '2', '2016-01-13'),
(3, '124', '1', '2016-01-14'),
(4, '124', '2', '2016-01-15'),
(5, '124', '3', '2016-01-16'),
(6, '125', '1', '2016-01-17')
;
CREATE TABLE myTable2 (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
myTable_id INT
) ENGINE MyISAM;
INSERT INTO myTable2
(`id`, `myTable_id`)
VALUES
(1, 1),
(2, 1),
(3, 2),
(4, 2),
(5, 3),
(6, 3),
(7, 4),
(8, 4),
(9, 4),
(10, 5),
(11, 6)
;
The field myTable2.myTable_id is a foreign key of myTable.Id.
I would like to get all the rows from myTable where myTable2.myTable_id = myTable.Id and the value of the field version in myTable is the maximum for every corresponding value for the field number in myTable.
I tried something like this:
SELECT
*
FROM
myTable,
myTable2
WHERE
myTable.version = (SELECT MAX(myTable.version) FROM myTable)
But the above query does not return the correct data. The correct query should output this:
Id number version date
2 123 2 2016-01-13
5 124 3 2016-01-16
6 125 1 2016-01-17
Please help!
One way to do this is to get the max version for each number in myTable in a derived table and join with that:
SELECT DISTINCT
m.*
FROM
myTable m
JOIN
myTable2 m2 ON m.id = m2.myTable_id
JOIN
(
SELECT number, MAX(version) AS max_version
FROM myTable
GROUP BY number
) AS derived_table
ON m.number = derived_table.number
AND m.version = derived_table.max_version
With your sample data this produces a result like this:
id number version date
6 125 1 2016-01-17
5 124 3 2016-01-16
2 123 2 2016-01-13
your Query is logically wrong. Here is the correct one
SELECT
*
FROM
myTable,
myTable2
WHERE
(myTable.version,myTable.number) in
(SELECT MAX(myTable.version),number FROM myTable group by number)
and myTable.id=myTable2.id
Here is the sqlfiddle http://sqlfiddle.com/#!9/74a67/4/0
This is the query posted for the previous edited question
SELECT * FROM myTable
inner join myTable2 on myTable.id = myTable2.mytable_id
WHERE (version, number) in
(SELECT MAX(version), number FROM myTable group by number)
Try this solution with using subquery simply as:
# Selecting desired result..
SELECT t1.id, t1.number, t1.version, t1.date
FROM myTable As t1 JOIN
# subquery to select max version and its corresponding
# number form myTable
(SELECT number, max(version) As max_ver FROM myTable
GROUP BY number
) As t2 ON t1.number = t2.number and t1.version = t2.max_ver
# Now checking for foreign key..
WHERE t1.id IN (SELECT mytable_id FROM myTable2);
Was it helpful..

join two table to replace new value from 2th table if exist

I have two table like belew
Table1:
CId -- Name -- Price -- MId
1 A 100 -- 1
2 B 110 -- 1
3 C 120 -- 1
4 D 120 -- 2
Table2:
Id -- UserId -- CId -- Price
1 1 2 200
1 2 2 200
I want to get data from Table one But if there is a record in Table2 that refrenced to Table1 CId then Price of Table2 replace with Price of Table1.
For example my UserId is 1 AND MId is 1 if I get data by mentioned senario I should get in result;
1 A 100
2 B 200
3 C 120
SQL FIDDLE
try this
select t1.cid,t1.name,
case when t2.cid is null
then t1.price
else t2.price
end as Price
from table1 t1 left join table2 t2 on t1.cid =t2.cid
where t1.mid =1 AND (t2.UserId = 1 OR t2.UserId IS NULL);
You can get by left join where you check null value in second table. if second price is null then use first table's price.
SELECT t1.CId, t1.name
CASE WHEN t2.price IS NULL
THEN t1.price
ELSE t2.price END AS Price
FROM table1 t1
LEFT JOIN table2 t2
ON t1.CId = t2.CId
WHERE WHERE t1.MId = 1
AND (t2.UserId = 1 OR t2.UserId IS NULL);
Try This Hopeful this will work.
CREATE TABLE table1(
[cid] [int] NULL,
[Name] [nvarchar](50) NULL,
[price] [bigint] NULL,
[MID] [int] NULL
)
CREATE TABLE [table2](
[id] [int] NULL,
[userid] [int] NULL,
[CId] [int] NULL,
[price] [bigint] NULL
)
GO
INSERT [dbo].[table1] ([cid], [Name], [price], [MID]) VALUES (1, N'A', 100, 1)
GO
INSERT [dbo].[table1] ([cid], [Name], [price], [MID]) VALUES (2, N'B', 110, 1)
GO
INSERT [dbo].[table1] ([cid], [Name], [price], [MID]) VALUES (3, N'C', 120, 1)
GO
INSERT [dbo].[table1] ([cid], [Name], [price], [MID]) VALUES (4, N'D', 120, 2)
GO
INSERT [dbo].[table2] ([id], [userid], [CId], [price]) VALUES (1, 1, 2, 200)
GO
INSERT [dbo].[table2] ([id], [userid], [CId], [price]) VALUES (1, 2, 2, 200)
GO
and Query-----------------------------------
SELECT t1.cid, t1.Name,
case when t1.cid=t2.cid then t2.price else t1.price end as Price
FROM table1 t1
INNER JOIN table2 t2 ON t1.MID = t2.userid
where t2.userid=1

T-sql update only when matched with joined table 1 to 1

How can I efficiently update the table based on values from a join table
only when ID - identifier I use to join both of the tables matches perfectly
1 to 1. I mean when joined table has only one ID to the updated table?
DECLARE #T1 TABLE (
ID INT,
NAME VARCHAR(10),
Age int
)
INSERT INTO #T1 VALUES (1, 'Name', null)
DECLARE #T2 TABLE (
ID INT,
Age int
)
INSERT INTO #T2 VALUES (1, 28)
INSERT INTO #T2 VALUES (1, 29)
INSERT INTO #T2 VALUES (1, 30)
In this example table T2 has three records of the ID = 1 which corresponds to one ID
in T1.
And I would like to update T1 only when in T2 there is one record of ID = 1.
(I would like to avoid joining twice table t2 to solve this task ...)
Thanks!
;WITH T2
AS (SELECT ID,
MAX(Age) AS Age
FROM #T2
GROUP BY ID
HAVING COUNT(*) = 1)
UPDATE #T1
SET [#T1].Age = T2.Age
FROM #T1
JOIN T2
ON [#T1].ID = T2.ID

Categories