I have two tables .
Input:
I have joined with the calendar table and bring the data till current.
I need a output .
I tried a query with UNION and Aggregation but I need to query two times and aggregate the same table . Since the table is very big .Is there a option to do different way
SELECT ID ,PERIOD,SUM(AMOUNTYTD) AMOUNTYTD,SUM(AMOUNT) AMOUNT
FROM (
SELECT ID ,b.PERIOD,SUM(AMOUNT) AMOUNTYTD,0 AMOUNT
FROM transaction a RIGHT OUTER JOIN CALENDAR b
ON b.PERIOD<=a.PERIOD
UNION ALL
SELECT ID ,PERIOD,0,SUM(AMOUNT)
FROM transaction
GROUP BY ID,PERIOD
)
GROUP BY ID,PERIOD
Showing the periodic amount side by side with the cumulative amount is easy - actually you only need to be able to create the correct table with the periodic amounts, the cumulative amounts are a simple application of analytic sum.
The key to joining the calendar table to the "input" data is to use a partitioned outer join - notice the partition by (id) clause in the join of the two tables. This causes the "inputs" data to be partitioned into separate sub-tables, one for each distinct id; the outer join to the calendar table is done separately for each such sub-table, and then the results are combined with a logical "union all".
with
input (id, period, amount) as (
select 1, 202010, 100 from dual union all
select 1, 202011, 50 from dual union all
select 2, 202011, 400 from dual
)
, calendar (period) as (
select 202010 from dual union all
select 202011 from dual union all
select 202012 from dual union all
select 202101 from dual
)
select id, period, amountytd, amount
from (
select i.id, period, i.amount,
sum(i.amount) over (partition by i.id order by period)
as amountytd
from calendar c left outer join input i partition by (id)
using (period)
)
where amountytd is not null
order by id, period
;
ID PERIOD AMOUNTYTD AMOUNT
--- ---------- ---------- ----------
1 202010 100 100
1 202011 150 50
1 202012 150
1 202101 150
2 202011 400 400
2 202012 400
2 202101 400
You have the query - I have joined with the calendar table and bring the data till current. let us assume it as your_query
You can use analytical function on it as follows:
Select t.*,
Case when lead(amountytd) over (partition by id order by period) = amountytd
then null
else amountytd
end as amount
From (your_query) t
Related
Struggling with this subquery - it should be basic, but I'm missing something. I need to make these available as apart of a larger query.
I have customers, and I want to get the ONE transaction with the HIGHEST timestamp.
Customer
customer foo
1 val1
2 val2
Transaction
tx_key customer timestamp value
1 1 11/22 10
2 1 11/23 15
3 2 11/24 20
4 2 11/25 25
The desired of the query:
customer foo timestamp value
1 val1 11/23 15
2 val2 11/25 25
I successfully wrote a subquery to calculate what I needed by using multiple sub queries, but it is very slow when I have a larger data set.
I did it like this:
(select timestamp where transaction.customer = customer.customer order by timestamp desc limit 1) as tx_timestamp
(select value where transaction.customer = customer.customer order by timestamp desc limit 1) as tx_value
So how do I reduce this down to only calculating it once? In my real data set, i have 15 columns joined over 100k rows, so doing this over and over is not performant enough.
In Postgres, the simplest method is distinct on:
select distinct on (cust_id) c.*, t.timestamp, t.value
from transactions t join
customer c
using (cust_id)
order by cust_id, timestamp desc;
Try this query please:
SELECT
T.customer, T.foo, T.timestamp, T.value
FROM Transaction T
JOIN
(SELECT
customer, max(timestamp) as timestamp
from Transaction GROUP BY customer) MT ON
T.customer = MT.customer
AND t.timestamp = MT.timestamp
Customer table and Acct tables has global scope, they share and increment this value
Below is customer table, SEQ NO 1 is beginning of customer data, SEQ_NO 238 is beginning of another customer data
Another is account table, all accounts with their SEQ_NOs inside a boundary of customer get same group (I want to group those accounts to the same customer, so that I can use listAgg to concatenate account id.), for example, below from SEQ_NO 2 and NO 224 (inclusive) should be assigned to the same group.
Is there a SQL way to do that, The worst case I was thinking is to define oracle type, and using function do that.
Any help is appreciate.
If I understand your question correctly, you want to be able to assign rows in the account table to groups, one per customer, so that you can then aggregate based on these groups.
So, the question is how to identify to which customer each account belongs, based on the sequence boundaries given in the first table ("customer") and the specific account numbers in the second table ("account").
This can be done in plain SQL, and relatively easily. You need a join between the accounts table and a subquery based on the customers table. The subquery must show the first and the last sequence number allocated to each client; to do that, you can use the lead analytic function. A bit of care must be taken regarding the last customer, for whom there is no upper limit for the sequence numbers.
You didn't provide test data in a usable format, so I created sample data in the with clause below (which is not part of the query - it's just there as a placeholder for test data).
with
customer (cust_id, seq_no) as (
select 101, 1 from dual union all
select 102, 34 from dual union all
select 200, 58 from dual union all
select 130, 90 from dual
)
, account (acct_id, seq_no) as (
select 1003, 3 from dual union all
select 1005, 11 from dual union all
select 1007, 33 from dual union all
select 1008, 60 from dual union all
select 1103, 77 from dual union all
select 1140, 92 from dual union all
select 1145, 99 from dual
)
select c.cust_id,
listagg(a.acct_id, ',') within group (order by a.acct_id) as acct_list
from (
select cust_id, seq_no as lower_no,
lead(seq_no) over (order by seq_no) - 1 as upper_no
from customer
) c
left outer join account a
on a.seq_no between c.lower_no and nvl(c.upper_no, a.seq_no)
group by c.cust_id
order by c.cust_id
;
OUTPUT
CUST_ID ACCT_LIST
------- --------------------
101 1003,1005,1007
102
130 1140,1145
200 1008,1103
I have a table like this:
;WITH CTE AS
( SELECT *
FROM (VALUES(1,'BlueCar',NULL),
(2,'RedCar',NULL),
(3,NULL,'BlueCar'),
(4,'GreenCar',NULL),
(5,NULL,'RedCar'),
(6,'BlueCar',NULL)
) AS ValuesTable(Time,Buy,Sell)
)
SELECT *
FROM CTE
Time Buy Sell
1 BlueCar NULL
2 RedCar NULL
3 NULL BlueCar
4 GreenCar NULL
5 NULL RedCar
6 BlueCar NULL
How can I query this table to get the total number of cars still in stock? The Time column is days since the shop opened. The time that the car was purchased must be preserved
Note: The input data is such that there will never be a situation where there are multiple cars in the inventory.
Expected Output
Time Buy
4 GreenCar
6 BlueCar
In the query below, I do two separate aggregations to obtain the buy and sell counts for each car. I left join buys to sells, which should not run the risk of losing data assuming that the dealer did not short sell any inventory which does not actually exist.
Then I join that result to a CTE which finds the latest time for each car. This would then correspond to the time when the most recent car came into inventory, for each car type.
I also include the inventory count, which you did request, but it may be useful for you if you decide to expand the scope of your query later on.
WITH yourTable AS (
SELECT 1 AS Time, 'BlueCar' AS Buy, NULL AS Sell UNION ALL
SELECT 2,'RedCar',NULL UNION ALL
SELECT 3,NULL,'BlueCar' UNION ALL
SELECT 4,'GreenCar',NULL UNION ALL
SELECT 5,NULL,'RedCar' UNION ALL
SELECT 6,'BlueCar',NULL
),
cte AS (
SELECT Buy, Time
FROM
(
SELECT Buy, Time,
ROW_NUMBER() OVER (PARTITION BY Buy ORDER BY Time DESC) rn
FROM yourTable
) t
WHERE rn = 1
)
SELECT
t1.Buy,
t1.buy_cnt - COALESCE(t2.sell_cnt, 0) AS inventory,
t3.Time
FROM
(
SELECT Buy, COUNT(*) AS buy_cnt
FROM yourTable
GROUP BY Buy
) t1
LEFT JOIN
(
SELECT Sell, COUNT(*) AS sell_cnt
FROM yourTable
GROUP BY Sell
) t2
ON t1.Buy = t2.Sell
LEFT JOIN cte t3
ON t1.Buy = t3.Buy
WHERE
t1.Buy IS NOT NULL AND
t1.buy_cnt - COALESCE(t2.sell_cnt, 0) > 0
ORDER BY
t3.Time;
Output:
Demo here:
Rextester
You can do this with a not exists:
;WITH CTE AS
( SELECT *
FROM (VALUES(1,'BlueCar',NULL),
(2,'RedCar',NULL),
(3,NULL,'BlueCar'),
(4,'GreenCar',NULL),
(5,NULL,'RedCar'),
(6,'BlueCar',NULL)
) AS ValuesTable(Time,Buy,Sell)
)
SELECT
[Time], Buy
FROM CTE as T1
WHERE
NOT EXISTS (SELECT 1 FROM CTE as T2 WHERE T2.TIME > T1.TIME AND T1.Buy = T2.Sell) AND
BUY IS NOT NULL
Presumably, you want:
with cte as (
. . .
)
select count(buy) - count(sell)
from cte;
Note: This does not verify that what you sell is something that has already been bought. It just counts up the non-NULL values in each column and takes the difference.
To get the stock at a certain point in time you can do
SELECT car, SUM(Inc) total FROM
(SELECT ID, Buy car, 1 Inc FROM tbl WHERE Buy>''
UNION ALL
SELECT ID, Sell car, -1 Inc FROM tbl WHERE Sell>'') coll
WHERE ID < 20 -- some cut-off time
GROUP BY car
I combine the two columns Buy and Sell into one (= car) and add another column (inc) with the increment of each action (-1 or 1). The rest is simple: select with a group by [car] and summation over column inc.
Here is a little demo: http://rextester.com/LLQDW60692
It is Good Question. I like that. Time by time your expected outputs changes.Its ok.
check below simple query for your problem.
Using Joins and Rownumber() we can achieve this.
;with CTE as
(
select a.time,a.buy,a.rid,COALESCE(b.rid,0)rid2 ,coalesce(b.sell,a.buy)sell from
( select time,buy,ROW_NUMBER()over( partition by buy order by (select 1)) rid
from #tableName where buy is not null)a left join
( select time,sell, ROW_NUMBER()over( partition by sell order by (select 1)) rid
from #TableName
where sell is not null )b on a.buy=b.sell
)
select Time,Buy from CTE
where rid!=rid2
Sample Demo For All Your Expected outputs.
Demo Link : Click Here
ALL Required Outputs :
I have two tables like the following:
PAY_TABLE
EMPLID PAY
123 100
123 150
123 150
DEDUCTION_TABLE
EMPLID DEDUCTION
123 15
123 30
and I want a result like the following:
TOTAL_PAY
400
I would like to get that result with a fairly simple query and I feel like I'm missing an obvious way to do it, but I can't seem to figure out what is.
For instance, this query returns 800 because every row in the PAY_TABLE is being duplicated when joined to the DEDUCTION_TABLE:
SELECT SUM(PAY) AS TOTAL_PAY
FROM PAY_TABLE JOIN DEDUCTION_TABLE USING(EMPLID);
And this query returns 250 because the DISTINCT keyword causes the second 150 value in the PAY_TABLE to be ignored:
SELECT SUM(DISTINCT PAY) AS TOTAL_PAY
FROM PAY_TABLE JOIN DEDUCTION_TABLE USING(EMPLID);
There are probably several ways to do this, but I am looking for the simplest way to return a result of 400.
Here is some code to create the example tables to make it easier:
WITH
PAY_TABLE AS (
SELECT 123 AS EMPLID, 100 AS PAY FROM DUAL
UNION ALL
SELECT 123, 150 FROM DUAL
UNION ALL
SELECT 123, 150 FROM DUAL
),
DEDUCTION_TABLE AS (
SELECT 123 AS EMPLID, 15 AS DEDUCTION FROM DUAL
UNION ALL
SELECT 123, 30 FROM DUAL
)
It's unclear exactly what you need, since your example doesn't make use of the DEDUCTION_TABLE table, but I believe what you'll want is to aggregate before you JOIN:
;with pay AS (SELECT EmplID,SUM(PAY) AS Pay
FROM PAY_TABLE
GROUP BY EmplID
)
,ded AS (SELECT EmplID,SUM(DEDUCTION) AS Ded
FROM DEDUCTION_TABLE
GROUP BY EmplID
)
SELECT *
FROM pay
LEFT JOIN ded
ON pay.EmplID = ded.EmplID
Assuming you need the join to DEDUCTION_TABLE just to ensure that there is a deduction for the employee:
SELECT SUM(P.PAY) AS TOTAL_PAY
FROM PAY_TABLE P
WHERE EXISTS (SELECT NULL FROM DEDUCTION_TABLE D
WHERE D.EMPLID = P.EMPLID;
I have a table with two columns, customer id and order.
Let's say I have in total order IDs 1,2,3,4
All the customer can have all the four orders, like below:
1234 1
1234 2
1234 3
1234 4
3245 3
3245 4
5436 2
5436 4
You can see above that 3245 customer doesn't have order id 1 or 2.
How could I print in the query output like:
3245 1
3245 2
5436 1
5436 3
EDIT: I don't have an order table, but I have a list of order's like we can hard code it in the query(1,2,3,4).
SELECT c.id, o.order
FROM (
SELECT 1 AS order
UNION ALL
SELECT 2 AS order
UNION ALL
SELECT 3 AS order
UNION ALL
SELECT 4 AS order
) o
CROSS JOIN
(
SELECT DISTINCT id
FROM customer_orders
) c
WHERE NOT EXISTS
(
SELECT NULL
FROM customer_orders ci
WHERE ci.id = c.id
AND ci.order = o.order
)
If you have customers table, it becomes more simple:
SELECT c.id, o.order
FROM (
SELECT 1 AS order
UNION ALL
SELECT 2 AS order
UNION ALL
SELECT 3 AS order
UNION ALL
SELECT 4 AS order
) o
CROSS JOIN
customers c
WHERE NOT EXISTS
(
SELECT NULL
FROM customer_orders ci
WHERE ci.id = c.id
AND ci.order = o.order
)
Okay, there are two issues here. The first problem is turning a list of numbers into a rowset. There are a number of different ways of doing this, depending on how you get the numbers into the query. In the following example I use a function which turns a comma-separated string into a nested table, which can be treated as a regular table with the TABLE() function. This is not strictly relevant to the question you pose. If you're interested in this bit of the implementation see my post in this other thread.
The second part of the problem is identifying the missing Orders for each Customer. The obvious approaches - such as using NOT IN with a sub-query - won't work, because the Orders for Customer 1234 match all the Order IDs. We need to do is fill in the missing orders for each Customer. This can be done by using a LEFT OUTER JOIN combined with the PARTITION BY clause. It is then a simple matter to filter out the hits by embedding the LOJ query in an outer SELECT, like this:
SQL> select customer_id
2 , missing_order_id
3 from (
4 select t42.customer_id
5 , t42.order_id
6 , nos.column_value as missing_order_id
7 from ( select * from table (str_to_number_tokens('1,2,3,4'))) nos
8 left outer join t42 partition by ( t42.customer_id )
9 on nos.column_value = t42.order_id
10 )
11 where order_id is null
12 /
CUSTOMER_ID MISSING_ORDER_ID
----------- ----------------
3245 1
3245 2
5436 1
5436 3
SQL>
aside from my comment, and your existing table, I would approach something like this...
select distinct
a.Customer,
b.OrderNumber
from
YourOrderTable a,
( select distinct OrderNumber from YourOrderTable ) b
where
b.OrderNumber NOT IN
( select OrderNumber from
YourOrderTable c
where a.Customer = c.Customer
and b.OrderNumber = c.OrderNumber )
By doing a select distinct as the second table in the FROM clause and no specific join to it, you will get a Cartesian join... ie: for each customer, it will join to every possible order number.
Then, in your WHERE clause, the NOT IN SQL test will only allow the "b." order numbers where none exist in the SQL-subselect (c.)
This could be a very costly query, especially if you have many unique orders..