I have a below table.
Now I need output like below.
Now I need to display the product differences between the consecutive dates based on product group by. For example: in an Apple loop, the first row should return the default value of 3 and then the next row should return the difference value. Similarly, the code should work on for the next product Samsung loop. Then, the result should be passed into a new column as displayed in the required output.
I am totally new to MySQL, however, I have tried my best to use a join to get the desired output, but I have no idea from where to start. Please guide me on this new concept.
Below are my code which I have tried.
CREATE TABLE products (
id numeric,
product text,
date_value DATE,
prod_value numeric);
INSERT INTO products(id, product, date_value, prod_value)
VALUES(1, 'Apple', '2018-01-22', 3);
INSERT INTO products(id, product, date_value, prod_value)
VALUES(2, 'Apple', '2018-01-23', 6);
INSERT INTO products(id, product, date_value, prod_value)
VALUES(3, 'Apple', '2018-01-24', 7);
INSERT INTO products(id, product, date_value, prod_value)
VALUES(4, 'Samsung', '2018-01-22', 1);
INSERT INTO products(id, product, date_value, prod_value)
VALUES(5, 'Samsung', '2018-01-23', 5);
SELECT
current.prod_value,(current.prod_value - previous.prod_value) diff
FROM
products previous
JOIN
products current
ON
current.id = previous.id+1;
You can use correlated sub-query (for older version), if you are runing with newer version of MySQL then you can use lag() :
select p.*,
p.prodvalue - (select coalesce(p2.prodvalue, 0)
from product p1
where p1.product = p.product and p1.id < p.id
order by p1.id desc
limit 1
) as proddiff
from product p;
Use lag():
select p.*,
(p.prod_value -
lag(p.prod_value, 1, 0) over (partition by product order by date_value)
) as prod_diff
from products p;
I have a table in which I have 3 columns
ID Amount District
1 100 A
2 500 B
1 250 A
2 240 B
1 100 A
Now I want to display Subtotal of distinct district and Grand total. Please guide me how should I write my sql query.
I want to show output as-
District Subtotal Grand Total
A 450
B 740 1190
You can use the SUM function to get the grand total, like this:
SELECT SUM(Amount) FROM data;
That'll return a single row containing a single number, the total of all of the Amounts in your entire table.
To get the per-district total, combine SUM with a GROUP BY clause:
SELECT SUM(Amount), District FROM data GROUP BY District;
This will return a table with one row for each district containing the total for that district.
Declaring the sample table and insert some data
DECLARE #tbl TABLE (ID INT, Amount INT, District CHAR(1))
INSERT #tbl
SELECT 1, 100, 'A' UNION ALL
SELECT 2, 500, 'B' UNION ALL
SELECT 1, 250, 'A' UNION ALL
SELECT 2, 240, 'B' UNION ALL
SELECT 1, 100, 'A'
Query
SELECT DISTINCT t.District,
CASE WHEN ID = (SELECT TOP 1 ID FROM #tbl
WHERE District = t.District
ORDER BY ID DESC)
THEN (SELECT SUM(Amount)
FROM #tbl
WHERE ID <= t.ID
AND District = t.District)
ELSE ' ' END AS [Subtotal],
CASE WHEN ID = (SELECT TOP 1 ID FROM #tbl
ORDER BY District DESC)
THEN (SELECT SUM(Amount)
FROM #tbl)
ELSE ' ' END AS [Grand Total]
FROM #tbl AS t
You can do the same thing for MySQL but instead of using TOP you need to use LIMIT
If you want the data in this format you can try using below code :
SELECT District As 'District', SUM(Amount) as 'Total'
FROM tbl
GROUP BY District
UNION
SELECT 'Grand Total', sum(Amount)
FROM tbl
Bellow is an example table.
ID FROM TO DATE
1 Number1 Number2 somedate
2 Number2 Number1 somedate
3 Number2 Number1 somedate
4 Number3 Number1 somedate
5 Number3 Number2 somedate
Expected result is to get 1 row for each unique pair of TO and FROM columns
Example result if ordered by ID ASC
(1,Number1,Number2)
(4,Number3,Number1)
(5,Number3,Number2)
Ok I have found how to do this with the following query
SELECT * FROM table GROUP BY LEAST(to,from), GREATEST(to,from)
However I am not able to get the most recent record for every unique pair.
I have tried with order by ID desc but it returns the first found row for unique pair.
SQL fiddle isn't working for some reason so in the mean time you will need to help me to help you.
Assuming that the following statement works
SELECT
LEAST(to,from) as LowVal,
GREATEST(to,from) as HighVal,
MAX(date) as MaxDate
FROM table
GROUP BY LEAST(to,from), GREATEST(to,from)
then you could join to that as
select t.*
from
table t
inner join
(SELECT
LEAST(to,from) as LowVal,
GREATEST(to,from) as HighVal,
MAX(date) as MaxDate
FROM table
GROUP BY LEAST(to,from), GREATEST(to,from)
) v
on t.date = v.MaxDate
and (t.From = v.LowVal or t.From = v.HighVal)
and (t.To = v.LowVal or t.To= v.HighVal)
I believe the following would work, my knowledge is with Microsoft SQL Server, not MySQL. If MySQL lacks one of these, let me know and I'll delete the answer.
DECLARE #Table1 TABLE(
ID int,
Too varchar(10),
Fromm varchar(10),
Compared int)
INSERT INTO #Table1 values (1, 'John','Mary', 2), (2,'John', 'Mary', 1), (3,'Sue','Charles',1), (4,'Mary','John',3)
SELECT ID, Too, Fromm, Compared
FROM #Table1 as t
INNER JOIN
(
SELECT
CASE WHEN Too < Fromm THEN Too+Fromm
ELSE Fromm+Too
END as orderedValues, MIN(compared) as minComp
FROM #Table1
GROUP BY CASE WHEN Too < Fromm THEN Too+Fromm
ELSE Fromm+Too
END
) ordered ON
ordered.minComp = t.Compared
AND ordered.orderedValues =
CASE
WHEN Too < Fromm
THEN Too+Fromm
ELSE
Fromm+Too
END
I used an int instead of time value, but it would work the same. It's dirty, but it's giving me the results I expected.
The basics of it, is to use a derived query where you take the two columns you want to get unique values for and use a case statement to combine them into a standard format. In this case, earlier alphabetical concatenated with the later value alphabetically. Use that value to get the minimum value we are looking for, join back to the original table to get the values separated out again plus whatever else is in that table. It is assuming the value we are aggregating is going to be unique, so in this case if there was (1, 'John', 'Mary', 2) and (2, 'Mary', 'John', 2), it would kind of break and return 2 records for that couple.
This answer was originally inspired by Get records with max value for each group of grouped SQL results
but then I looked further and came up with the correct solution.
CREATE TABLE T
(`id` int, `from` varchar(7), `to` varchar(7), `somedate` datetime)
;
INSERT INTO T
(`id`, `from`, `to`, `somedate`)
VALUES
(1, 'Number1', 'Number2', '2015-01-01 00:00:00'),
(2, 'Number2', 'Number1', '2015-01-02 00:00:00'),
(3, 'Number2', 'Number1', '2015-01-03 00:00:00'),
(4, 'Number3', 'Number1', '2015-01-04 00:00:00'),
(5, 'Number3', 'Number2', '2015-01-05 00:00:00');
Tested on MySQL 5.6.19
SELECT *
FROM
(
SELECT *
FROM T
ORDER BY LEAST(`to`,`from`), GREATEST(`to`,`from`), somedate DESC
) X
GROUP BY LEAST(`to`,`from`), GREATEST(`to`,`from`)
Result set
id from to somedate
3 Number2 Number1 2015-01-03
4 Number3 Number1 2015-01-04
5 Number3 Number2 2015-01-05
But, this relies on some shady behavior of MySQL, which will be changed in future versions. MySQL 5.7 rejects this query because the columns in the SELECT clause are not functionally dependent on the GROUP BY columns. If it is configured to accept it (ONLY_FULL_GROUP_BY is disabled), it works like the previous versions, but still it is not guaranteed: "The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate."
So, the correct answer seems to be this:
SELECT T.*
FROM
T
INNER JOIN
(
SELECT
LEAST(`to`,`from`) AS LowVal,
GREATEST(`to`,`from`) AS HighVal,
MAX(somedate) AS MaxDate
FROM T
GROUP BY LEAST(`to`,`from`), GREATEST(`to`,`from`)
) v
ON T.somedate = v.MaxDate
AND (T.From = v.LowVal OR T.From = v.HighVal)
AND (T.To = v.LowVal OR T.To = v.HighVal)
Result set is the same as above, but in this case it is guaranteed to stay like this, while before you could easily get different date and id for row Number2, Number1, depending on what indexes you have on the table.
It will work as expected until you have two rows in the original data that have exactly the same somedate and to and from.
Let's add another row:
INSERT INTO T (`id`, `from`, `to`, `somedate`)
VALUES (6, 'Number1', 'Number2', '2015-01-03 00:00:00');
The query above would return two rows for 2015-01-03:
id from to somedate
3 Number2 Number1 2015-01-03
6 Number1 Number2 2015-01-03
4 Number3 Number1 2015-01-04
5 Number3 Number2 2015-01-05
To fix this we need a method to choose only one row in the group. In this example we can use unique ID to break the tie. If there are more than one rows in the group with the same maximum date we will choose the row with the largest ID.
The inner-most sub-query called Groups simply returns all groups, like original query in the question. Then we add one column id to this result set, and we choose id that belongs to the same group and has highest somedate and then highest id, which is done by ORDER BY and LIMIT. This sub-query is called GroupsWithIDs. Once we have all groups and an id of the correct row for each group we join this to the original table to fetch the rest of the column for found ids.
final query
SELECT T.*
FROM
(
SELECT
Groups.N1
,Groups.N2
,
(
SELECT T.id
FROM T
WHERE
LEAST(`to`,`from`) = Groups.N1 AND
GREATEST(`to`,`from`) = Groups.N2
ORDER BY T.somedate DESC, T.id DESC
LIMIT 1
) AS id
FROM
(
SELECT LEAST(`to`,`from`) AS N1, GREATEST(`to`,`from`) AS N2
FROM T
GROUP BY LEAST(`to`,`from`), GREATEST(`to`,`from`)
) AS Groups
) AS GroupsWithIDs
INNER JOIN T ON T.id = GroupsWithIDs.id
final result set
id from to somedate
4 Number3 Number1 2015-01-04
5 Number3 Number2 2015-01-05
6 Number1 Number2 2015-01-03
I wrote a query that is throwing an error when it can't find a record:
Error
Column 'product_id' cannot be null
MySQL
INSERT INTO orders (date, product_id, quantity)
VALUES ('11/29/2012', (
SELECT product_id FROM products WHERE product_name = 'Oranges'
), 12)
;
I'm actually iterating in my PHP and some of the product_name records are not going to exist.
Can I say somehow that if the subquery returns nothing, gracefully stop/abort so the PHP can keep iterating?
Try this:
INSERT INTO orders (date, product_id, quantity)
SELECT '11/29/2012', product_id, 12
FROM products
WHERE product_name = 'Oranges'
If there is no matching product, the query will succeed but return no rows modified. If you wish you can read the number of rows modified from PHP when you execute the query.
Notice that if there are multiple products with product_name = 'Oranges' you'll get multiple rows inserted into your table.
If you want a record to be inserted anyway, you can try to fallback to e.g. 0 if no such product exists:
INSERT INTO orders (date, product_id, quantity)
VALUES ('11/29/2012', COALESCE((
SELECT product_id FROM products WHERE product_name = 'Oranges'
), 0), 12);
For example: If I have a table where contains:
PRICES
1
5
3
8
2
8
If I'd like the the second element, how can I get it? Only that number.. Is it possible?
Try this:
declare #x int
set #x = 3
select top 1
from (select top #x from table order by 1 desc) xx
$third_element = mysql_result(mysql_query("SELECT prices FROM the_table WHERE prices = '3'"), 0);
That selects the element, but I don't know why you would want to do it like that unless you had another row to select it from like, WHERE other_row = 'something', then you would arrive at 3.
yes it is possible
here is the solution for your answer try this.
create table prices
(
price int
)
insert into prices values (1)
insert into prices values (5)
insert into prices values (3)
insert into prices values (8)
insert into prices values (2)
insert into prices values (8)
select x.* from
(
select ROW_NUMBER()over(order by price) as RowNumber,price
from prices
)x
where x.RowNumber=3