Select data with calculation in same data table - sql

My Table is
itemcode in out sum value datetime PP
-------|---|---|-----------|-----------|---------
A1234 | 1 | 0 | 100 |04/03/2009 | E01
A1234 | 0 | 1 | -100 |05/03/2009 | E01
A1234 | 1 | 0 | 100 |06/03/2009 | E01
A1234 | 0 | 1 | -100 |07/03/2009 | E01
A1234 | 1 | 0 | 100 |08/03/2009 | E01
A1234 | 9 | 0 | 900 |09/03/2009 | S01
A1234 | 0 | 3 | -300 |10/03/2009 | S01
A1234 | 8 | 0 | 800 |11/03/2009 | S01
A1235 | 9 | 0 | 900 |12/03/2009 | E01
A1235 | 0 | 2 | -200 |13/03/2009 | E01
A1235 | 0 | 3 | -300 |14/03/2009 | E01
A1235 | 8 | 0 | 800 |15/03/2009 | S01
Result would be:
itemcode Remain sum value datetime PP
---------|--------|-----------|-----------|----
A1234 | 1 | 100 |08/03/2009 | E01
A1234 | 6 | 600 |09/03/2009 | S01
A1234 | 8 | 800 |11/03/2009 | S01
A1235 | 4 | 400 |12/03/2009 | E01
A1235 | 8 | 800 |15/03/2009 | S01
How to select such data and calculate how many stock remaining, group by same itemcode and pp,
list out in different in stock date

The way I would do this is use the SUM() function to sum the in column, as well as the out column, and subtract them to get the value that is in stock. Once you've done that, you can use the GROUP BY clause to group the columns you want. Intuitively, it works pretty closely to what you described.
You'll also have to do something with the datecolumn, and it seems that you want to produce the latest date in each group, so I've also selected the max date for that reason.
You didn't list the column names in your question, so I'm going on a limb here:
SELECT itemcode, (SUM(inColumn) - SUM(outColumn)) AS inStock, SUM(value) AS value, MAX(dateColumn) AS latestDate, pp
FROM myTable
GROUP BY itemcode, pp;
Here is the SQL Fiddle.

Related

Selecting the best-performing sales rep by date

Given two tables, sales_reps and sales:
sales_reps
+--------+-------+
| rep_id | name |
+--------+-------+
| 1 | Tony |
+--------+-------+
| 2 | Jim |
+--------+-------+
| 3 | Laura |
+--------+-------+
| 4 | Sam |
+--------+-------+
sales
+------------+----------+--------+-------------+
| sale_date | sales_id | rep_id | sale_amount |
+------------+----------+--------+-------------+
| 2021-01-01 | 1 | 1 | 2000 |
+------------+----------+--------+-------------+
| 2021-01-01 | 2 | 1 | 4000 |
+------------+----------+--------+-------------+
| 2021-01-01 | 3 | 2 | 3000 |
+------------+----------+--------+-------------+
| 2021-01-01 | 4 | 1 | 1000 |
+------------+----------+--------+-------------+
| 2021-01-01 | 5 | 4 | 5000 |
+------------+----------+--------+-------------+
| 2021-01-02 | 6 | 3 | 10000 |
+------------+----------+--------+-------------+
| 2021-01-02 | 7 | 3 | 10000 |
+------------+----------+--------+-------------+
| 2021-01-02 | 8 | 2 | 4000 |
+------------+----------+--------+-------------+
| 2021-01-02 | 9 | 1 | 6000 |
+------------+----------+--------+-------------+
| 2021-01-02 | 10 | 4 | 2000 |
+------------+----------+--------+-------------+
| 2021-01-03 | 11 | 2 | 8000 |
+------------+----------+--------+-------------+
| 2021-01-03 | 12 | 2 | 1000 |
+------------+----------+--------+-------------+
| 2021-01-03 | 13 | 3 | 4500 |
+------------+----------+--------+-------------+
| 2021-01-03 | 14 | 3 | 4500 |
+------------+----------+--------+-------------+
I want to show how many unique reps made sales by date (easy) and the rep_id and name of the rep who generated the highest total sales amount for each date. If more than one rep generated the same greatest total sales amount for a date, I want to show the lesser rep_id and that rep's name. The results should look like this:
+------------+-------------+----------+----------+
| sale_date | unique_reps | best_rep | rep_name |
+------------+-------------+----------+----------+
| 2021-01-01 | 3 | 1 | Tony |
+------------+-------------+----------+----------+
| 2021-01-02 | 4 | 3 | Laura |
+------------+-------------+----------+----------+
| 2021-01-03 | 2 | 2 | Jim |
+------------+-------------+----------+----------+
Laura and Jim both generated $9,000 in sales on 2021-01-03. But Jim's rep_id is 2, which is less than Laura's rep_id of 3. So Jim is displayed as the rep who generated the greatest sales amount on 2021-01-03.
Postgres has a mode() function, but it doesn't allow you to choose which rep to choose in the case of ties. For that, you can be more explicit:
select distinct on (s.sale_date) s.sale_date,
s.rep_id, sr.name,
count(*) over (partition by s.date) as num_reps
from sales s join
sales_reps sr
on s.rep_id = sr.rep_id
group by s.sale_date, s.rep_id
order by s.sale_date, sum(s.sale_amount) desc, s.rep_id, sr.name;
What is this doing? It is aggregating by the date and sales rep. Because of the distinct on, though, it is only taking one row per date. In this row:
count(*) over (partition by date) counts the number of reps (it is counting the rows after the aggregation).
s.rep_id is chosen based on the order by criteria -- first the most sales, then by the lowest rep id.
sr.name is the name of the sales rep.

SQL postgresql: maximum value and sum of values

I need to make a query to the following table, to return the maximum date grouped by code and also make the following calculation: deb-cre (maximum only).
How would I do this?
code | date | deb | cred
-----------------------------------
4 | 2018-01-01 | 100,00 | 200,00
4 | 2017-12-28 | 100,00 | 500,00
6 | 2018-01-23 | 350,00 | 400,00
6 | 2018-04-28 | 140,00 | 678,00
8 | 2018-01-12 | 156,00 | 256,00
8 | 2016-02-28 | 134,00 | 598,00
The result must be
4 | 2018-01-01 | -200,00
6 | 2018-04-28 | -50,00
8 | 2018-01-12 | -464,00
PostgreSQL's DISTINCT ON in combination with ORDER BY will return the first row per group:
SELECT DISTINCT ON (code)
code, date, deb - cre
FROM your_table
ORDER BY code, date DESC;

postgresql - Change single row to multiple rows

I have a table named payment_info, with the following records.
paymentid | customercode | previousbalance | paymentamount | remainingbalance
-----------------------------------------------------------------------------
PID0001 | CUST024 | 10000 | 2500 | 7500
PID0002 | CUST031 | 8500 | 3500 | 5000
PID0003 | CUST005 | 12000 | 1500 | 10500
Then what I want is to create a 3 rows per row of the above table.
I want my results to look like this.
Payment Group | Payment Line Item | Payment ID | Customer Code | Type | Amount
--------------------------------------------------------------------------------------------------
1 | 1 | PID0001 | CUST024 | PREVIOUS BALANCE | 10000.00
1 | 2 | | | PAYMENT AMOUNT | 2500.00
1 | 3 | | | REMAINING BALANCE | 7500.00
2 | 1 | PID0002 | CUST031 | PREVIOUS BALANCE | 8500.00
2 | 2 | | | PAYMENT AMOUNT | 3500.00
2 | 3 | | | REMAINING BALANCE | 5000.00
3 | 1 | PID0003 | CUST005 | PREVIOUS BALANCE | 12000.00
3 | 2 | | | PAYMENT AMOUNT | 1500.00
3 | 3 | | | REMAINING BALANCE | 10500.00
Here is the query I've started. But it did not return results same as above.
select row_number() over() as id,paymentid,customercode,'PREVIOUS BALANCE' as type,previousbalance from payment_info
union
select row_number() over() as id,'','','PAYMENT AMOUNT' as type,paymentamount from payment_info
union
select row_number() over() as id,'','','REMAINING BALANCE' as type,remainingbalance from payment_info
Is there other ways, where I will not use UNION Keyword? Cause in the real table, I will be using 30+ columns, querying thousands of records.
I also don't know how to create auto generated number (id) from payment group (per payment id) and Payment Line Item (per group).
thanks
version with whitespace (empty text)
The unnest function can do this for you.
And if you want the empty text then you can use this
SELECT ROW_NUMBER() OVER (ORDER BY paymentid) AS "group",
unnest(array[1, 2, 3]) AS "line item",
unnest(array[paymentid, '', '']) AS "paymentid",
unnest(array[customercode, '', '']) AS "customercode",
unnest(array['PREVIOUS BALANCE', 'PAYMENT AMOUNT', 'REMAINING BALANCE']) AS "type",
unnest(array[previousbalance, paymentamount, remainingbalance]) AS "amount"
FROM payment_info
ORDER BY 1, 2 ;
To get this
group | line item | paymentid | customercode | type | amount
-------+-----------+-----------+--------------+-------------------+--------
1 | 1 | PID0001 | CUST024 | PREVIOUS BALANCE | 10000
1 | 2 | | | PAYMENT AMOUNT | 2500
1 | 3 | | | REMAINING BALANCE | 7500
2 | 1 | PID0002 | CUST031 | PREVIOUS BALANCE | 8500
2 | 2 | | | PAYMENT AMOUNT | 3500
2 | 3 | | | REMAINING BALANCE | 5000
3 | 1 | PID0003 | CUST005 | PREVIOUS BALANCE | 12000
3 | 2 | | | PAYMENT AMOUNT | 1500
3 | 3 | | | REMAINING BALANCE | 10500
If you want to have, for example points or other text, or arrows in the empty text columns, you can do this easily with unnest.
You can control the 4 empty text values individually.
SELECT ROW_NUMBER() OVER (ORDER BY paymentid) AS "group",
unnest(array[1, 2, 3]) AS "line item",
unnest(array[paymentid, ' a', ' c']) AS "paymentid",
unnest(array[customercode, ' b', ' d']) AS "customercode",
unnest(array['PREVIOUS BALANCE', 'PAYMENT AMOUNT', 'REMAINING BALANCE']) AS "type",
unnest(array[previousbalance, paymentamount, remainingbalance]) AS "amount"
FROM payment_info
ORDER BY 1, 2 ;
to generate
group | line item | paymentid | customercode | type | amount
-------+-----------+-----------+--------------+-------------------+--------
1 | 1 | PID0001 | CUST024 | PREVIOUS BALANCE | 10000
1 | 2 | a | b | PAYMENT AMOUNT | 2500
1 | 3 | c | d | REMAINING BALANCE | 7500
2 | 1 | PID0002 | CUST031 | PREVIOUS BALANCE | 8500
2 | 2 | a | b | PAYMENT AMOUNT | 3500
2 | 3 | c | d | REMAINING BALANCE | 5000
3 | 1 | PID0003 | CUST005 | PREVIOUS BALANCE | 12000
3 | 2 | a | b | PAYMENT AMOUNT | 1500
3 | 3 | c | d | REMAINING BALANCE | 10500
It's a very flexible solution, you know.
It isn't necessary to always use union queries. Here for example you can use 3 rows and a cross join instead. This has the advantage of only a single pass over the source table.
drop table if exists Table1;
CREATE TABLE Table1
("paymentid" varchar(7), "customercode" varchar(7)
, "previousbalance" int, "paymentamount" int, "remainingbalance" int)
;
INSERT INTO Table1
("paymentid", "customercode", "previousbalance", "paymentamount", "remainingbalance")
VALUES
('PID0001', 'CUST024', 10000, 2500, 7500),
('PID0002', 'CUST031', 8500, 3500, 5000),
('PID0003', 'CUST005', 12000, 1500, 10500)
;
select
paymentid
, customercode
, rn
, typeof
, case when rn = 1 then previousbalance
when rn = 2 then paymentamount
when rn = 3 then remainingbalance
end as Amount
from Table1
cross join (select 1 rn , 'previousbalance' typeof
union all
select 2 , 'paymentamount'
union all
select 3, 'remainingbalance'
) rns
That data/query produces this result:
+----+-----------+--------------+----+------------------+--------+
| | paymentid | customercode | rn | typeof | amount |
+----+-----------+--------------+----+------------------+--------+
| 1 | PID0001 | CUST024 | 1 | previousbalance | 10000 |
| 2 | PID0001 | CUST024 | 2 | paymentamount | 2500 |
| 3 | PID0001 | CUST024 | 3 | remainingbalance | 7500 |
| 4 | PID0002 | CUST031 | 1 | previousbalance | 8500 |
| 5 | PID0002 | CUST031 | 2 | paymentamount | 3500 |
| 6 | PID0002 | CUST031 | 3 | remainingbalance | 5000 |
| 7 | PID0003 | CUST005 | 1 | previousbalance | 12000 |
| 8 | PID0003 | CUST005 | 2 | paymentamount | 1500 |
| 9 | PID0003 | CUST005 | 3 | remainingbalance | 10500 |
+----+-----------+--------------+----+------------------+--------+
Please then note that SQL isn't a "report writer" so blanks in columns for "layout" are not a good fit for SQL which wants to repeat information (like you see above in the result) so that you can sort and filter as needed.

Sql query for special record

At the first excuse me for my bad english.
I have two tables:
master table:
| product id | pr_name | remain_Qty |
+--------------+------------------+-------------------+
| 1 | x | 13 |
| 2 | y | 18 |
| 3 | z | 21 |
+--------------+------------------+-------------------+
Detail Table (This table contain detail data of bought product):
+--------------+------------------+----------+--------+
| date | pr_id | Qty |price |
+--------------+------------------+----------+--------+
| 2010-01-01 | 1 | 3 | 1000 |
| 2010-01-02 | 1 | 5 | 1200 |
| 2010-01-01 | 2 | 11 | 1100 |
| 2010-01-03 | 1 | 4 | 1400 |
| 2010-01-04 | 3 | 3 | 1300 |
| 2010-01-01 | 2 | 6 | 1600 |
| 2010-01-03 | 1 | 7 | 1700 |
| 2010-01-02 | 3 | 3 | 1300 |
| 2010-01-01 | 3 | 5 | 1500 |
| 2010-01-04 | 3 | 7 | 1700 |
| 2010-01-06 | 2 | 8 | 1800 |
| 2010-01-07 | 2 | 4 | 1400 |
| 2010-01-03 | 1 | 3 | 1300 |
| 2010-01-04 | 3 | 6 | 1600 |
| 2010-01-08 | 1 | 1 | 1100 |
+--------------+------------------+----------+--------+
sum Qty of product 1 = 23
sum Qty of product 2 = 29
sum Qty of product 3 = 21
As a result I want list of the Details table, where the list is sorted by pr_id , date and price, but the sum(Qty) per pr_id don't exceed the remain_Qty of the product_id of the Master table.
For example:
+--------------+------------------+----------+--------+
| date | pr_id | Qty |price |
+--------------+------------------+----------+--------+
| 2010-01-01 | 1 | 3 | 1000 |
| 2010-01-02 | 1 | 5 | 1200 |
| 2010-01-03 | 1 | 4 | 1400 |
| 2010-01-03 | 1 | 1 | 1700 |
| 2010-01-01 | 2 | 11 | 1100 |
| 2010-01-01 | 2 | 6 | 1600 |
| 2010-01-01 | 3 | 5 | 1500 |
| 2010-01-02 | 3 | 3 | 1300 |
| 2010-01-04 | 3 | 3 | 1300 |
| 2010-01-04 | 3 | 7 | 1700 |
+--------------+------------------+----------+--------+
More of a clarification than a direct SQL answer. But what it LOOKS like they may be wanting is based on an inventory being depleted to fill orders from the known available quantity, but even that falls short as the may be missing a second qty of 3 on 2010-01-03 for product 1... which if looking at just ID=1 from his sample data would show...
| date | pr_id | Qty |price | Qty Available to fill order
+--------------+--------+-----+-------+
| 2010-01-01 | 1 | 3 | 1000 | 13 - 3 = 10 avail next order
| 2010-01-02 | 1 | 5 | 1200 | 10 - 5 = 5 avail next order
| 2010-01-03 | 1 | 3 | 1300 | 5 - 3 = 2 avail next order
| 2010-01-03 | 1 | 4 | 1400 | only 2 to PARTIALLY fill this order
| 2010-01-03 | 1 | 7 | 1700 | none available
| 2010-01-08 | 1 | 1 | 1100 | none available
With the extra sample record removed, would result in...
| date | pr_id | Qty |price | Qty Available to fill order
+--------------+--------+-----+-------+
| 2010-01-01 | 1 | 3 | 1000 | 13 - 3 = 10 avail next order
| 2010-01-02 | 1 | 5 | 1200 | 10 - 5 = 5 avail next order
| 2010-01-03 | 1 | 4 | 1400 | 5 - 4 = 1 avail for next order
| 2010-01-03 | 1 | 7 | 1700 | only 1 of the 7 available
| 2010-01-08 | 1 | 1 | 1100 | no more available...
So Aliasghar, does this better represent what you are trying to do??? Fill the available orders based on which order was entered into the system first, fill as many as possible based on inventory and stop there?
Please confirm by adding comment to this answer and maybe we can help resolve... Also, confirm WHICH Database are you using... SQL-Server, Oracle, MySQL, etc...
Here a working query for pr_id=1 , I used MySql:
select final.pr_date, final.pr_id, count(t_qty) as qty, final.price from
(select * FROM (select q.pr_date, q.pr_id, 1 as t_qty, q.price , #t := #t + t_qty total
FROM(
SELECT d.pr_date, d.pr_id, 1 as t_qty, d.price
FROM detail_table d
JOIN generator_4k i
ON i.n between 1 and d.qty
WHERE d.pr_id= 1
Order by d.id, d.pr_date) q
CROSS JOIN (SELECT #t := 0) i) c
WHERE c.total <= (select remain_qty from master_table WHERE product_id = 1)) final
group by final.pr_date , final.pr_id , final.price ;
Here SQL FIDDLE
You have to adapt your detail_table to add a technical id as primary key and create some views, I renamed the date column as pr_date, You'll find the schema on the sql fiddle.
Here another query Using SQL SERVER
select final.pr_date, final.pr_id, count(t_qty) as qty, final.price from
(SELECT top(select remain_qty from master_table WHERE product_id = 1) d.pr_date, d.pr_id, 1 as t_qty, d.price
FROM detail_table d
JOIN generator_4k i
ON i.n between 1 and d.qty
WHERE d.pr_id= 1
Order by d.id, d.pr_date) final
group by final.pr_date , final.pr_id , final.price ;
Here SQL FIDDLE
Daywise product by info
Beneath a suggested statement.
select t2.date,t2.pr_id,t1.pr_name,sum(qty) as qty_buy,sum(price) as amount from master_table as t1
inner join detail_table as t2 on t1.product_id=t2.pr_id
group by t2.date,t2.pr_id
order by t1.date,t2.pr_id
I had a hard time to understand what you really wanted.
So if I understood well, you want some data that correspond to a product but do not go over your remained item.
So I coudn't bypass yet the first query that goes over, and only take the remaining from it.
So my query for now just stop until it s get to the remained items allowed
SQL FIDDLE
To be able to do what you want, you need to first create a view that generate row based on your quantity.
Like something like
> +--------------+------------------+----------+--------+
| date | pr_id | Qty |price |
+--------------+------------------+----------+--------+
| 2010-01-01 | 1 | 3 | 1000 |
turn into something like
> +--------------+------------------+----------+--------+
| date | pr_id | Qty |price |
+--------------+------------------+----------+--------+
| 2010-01-01 | 1 | 1 | 1000
| 2010-01-01 | 1 | 1 | 1000 |
| 2010-01-01 | 1 | 1 | 1000 |
Then you count your rows until your remained item allows you to do it.
After you regroup all of the row by price,pr_id and date.
VOILA

Query to find the first date after a specific grouped sum value

I have an article table that holds the current stock for each article. I need to know the last date when new stock has arrived, after running out of stock for that specific article.
The table looks like this.
+-----------+-----------------+-------+
| ArticleID | StockDate | Stock |
+-----------+-----------------+-------+
| 1 | 1/1/2012 10:15 | 100 |
| 1 | 2/1/2012 13:39 | -50 |
| 1 | 2/1/2012 15:17 | -50 |
| 1 | 4/1/2012 08:05 | 100 |
| 2 | 5/1/2012 09:48 | 50 |
| 1 | 6/1/2012 14:21 | -25 |
| 1 | 7/1/2012 16:01 | 10 |
| 2 | 8/1/2012 13:42 | -10 |
| 1 | 9/1/2012 09:56 | -85 |
| 1 | 13/1/2012 08:12 | 100 |
| 1 | 13/1/2012 10:50 | -15 |
+-----------+-----------------+-------+
The output should look like this.
+-----------+-----------------+
| ArticleID | StockDate |
+-----------+-----------------+
| 2 | 5/1/2012 09:48 |
| 1 | 13/1/2012 08:12 |
+-----------+-----------------+
How did i get this output? ArticleID 1 had a 100 in stock but reached 0 for the first time on 2/1/2012 15:17. Then new stock arrived and it hit 0 again at 9/1/2012 09:56. So the result should shows the first date after the last empty stock grouped by ArticleID. ArticleID 2 didn't had a 0 point, so the first stock date is shown.
I need a result set that can be joined with other queries. So a Stored Procedure does not work for me.
select ArticleID,stockdate from
(
select t.ArticleID, t.stockdate, ROW_NUMBER() Over (partition by t.articleid order by v.articleid desc, stockdate) rn
from yourtable t
left join
(
select ArticleID, MAX(stockdate) as msd from yourtable t1
cross apply (select sum(stock) as stockrt from yourtable where stockdate<=t1.stockdate and ArticleID=t1.ArticleID) rt
where stockrt = 0
group by articleid
) v
on t.ArticleID = v.ArticleID
and t.stockdate>v.msd
) v
where rn=1