SQL: how to separate combined row into individual rows - sql

I have a database table like this:
id | check_number | amount
1 | 1001]1002]1003 | 200]300]100
2 | 2001]2002 | 500]1000
3 | 3002]3004]3005]3007 | 100]300]600]200
I want to separate the records into something like this:
id | check_number | amount
1 | 1001 | 200
2 | 1002 | 300
3 | 1003 | 100
. | . | .
. | . | .
. | . | .
How do I do this just using SQL in Oracle and SQL Server?
Thanks,
Milo

In Oracle Only, using the CONNECT BY LEVEL method (see here), with several caveats:
select rownum, id,
substr(']'||check_number||']'
,instr(']'||check_number||']',']',1,level)+1
,instr(']'||check_number||']',']',1,level+1)
- instr(']'||check_number||']',']',1,level) - 1) C1VALUE,
substr(']'||amount||']'
,instr(']'||amount||']',']',1,level)+1
,instr(']'||amount||']',']',1,level+1)
- instr(']'||amount||']',']',1,level) - 1) C2VALUE
from table
connect by id = prior id and prior dbms_random.value is not null
and level <= length(check_number) - length(replace(check_number,']')) + 1
ROWNUM ID C1VALUE C2VALUE
1 1 1001 200
2 1 1002 300
3 1 1003 100
4 2 2001 500
5 2 2002 1000
6 3 3002 100
7 3 3004 300
8 3 3005 600
9 3 3007 200
Essentially we blow out the query using the hierarchical functions of oracle and then only get the substrings for the data in each "column" of data inside the check_number and amount columns.
Major Caveat: The data to be transformed must have the same number of "data elements" in both columns, since we use the first column to "count" the number of items to be transformed.
I have tested this on 11gR2. YMMV depending on DMBS version as well. Note the need to use the "PRIOR" operator, which prevents oracle from going into an infinite connect by loop.

Related

How to transfer data from one column to another

I am trying to merge one column with another one but not sure what should I use insert or update statement.
The table looks like this
marketId | Product | Phone | Value |
1 washing machine null 800
1 air condition null 300
1 refrigerator null 600
1 TV null 500
2 washing machine null 850
2 air condition null 300
2 refrigerator null 600
2 TV null 500
I want to get result like this -
marketId | Product | Value |
1 washing machine 800
1 air condition 300
1 refrigerator 600
1 TV 500
1 Phone null
2 washing machine 850
2 air condition 200
2 refrigerator 550
2 TV 500
2 Phone null
Tried few things (update/insert statements), but I was unsuccessful. Do you have some other ideas?
Thanks!
You seem to want:
select market_id, product, value
from t
union all
select market_id, 'phone', null
from t;

select from table with between

please, help advice.
I have a table.
id|score_max|score_min| segment
--|---------|---------|--------
1 |264 | |girl
2 |263 | 250 |girl+
3 |249 | 240 |girl
4 | | 239 |girl
It is not necessary to obtain a value depending on the value of the score.
But it can be null.
For example, 260 is value from other table
select segment
from mytable
where score_max<260 and score_min>260
Output:
2 |263 | 250 |girl+
but if value =200, sql is not correct
How to make a request correctly?
For this sample data that makes more sense:
id|score_max|score_min| segment
--|---------|---------|--------
1 | | 264 |girl
2 |263 | 250 |girl+
3 |249 | 240 |girl
4 |239 | |girl
you can get the result that you want like this:
select *
from tablename
where
(? >= score_min or score_min is null)
and
(? <= score_max or score_max is null)
Replace ? with the value that you search for.
See the demo.

sum revenue based on criteria form another table Powerpivot

I have a model where I have Revenue table that has revenue2016 column
another table Programs where i have
program | min
I would like to add a calculated column to programs table so that it sums revenue that is grater than the min like so
=CALCULATE(SUM(Revenue[revenue2016 ]),Revenue[revenue2016]>=Programs[min])
this gave me an error
The data should look like this
#Revenue
Revenue
10
10
10
10
10
100
100
100
100
100
1000
1000
1000
1000
1000
#Programs
program | min | summed rev
a | 10 | 5550
b | 100 | 5500
c | 1000 | 5000
Just After I posted it I found the answer, I'll share it if someone else came across same issue
=calculate(sum(Revenue[revenue2016]),filter(Revenue,Revenue[revenue2016]>=Programs[Min]))

SQL Group By On Output From User Defined Function

Is it possible, in Oracle, to group data on the output of a user defined function? I get errors when I try to, and it best illustrated by the below example:
I am trying to interrogate results in table structure similar to below:
id | data
1000 | {abc=123, def=234, ghi=111, jkl=456, mno=567, pqr=678, stu=789, vwx=890, yza=901}
1000 | {abc=123, def=234, ghi=222, jkl=456, mno=567, pqr=678, stu=789, vwx=890, yza=901}
1000 | {abc=123, def=434, ghi=333, jkl=456, mno=567, pqr=678, stu=789, vwx=890, yza=901}
1000 | {abc=123, def=434, ghi=444, jkl=456, mno=567, pqr=678, stu=789, vwx=890, yza=901}
1000 | {abc=123, def=634, ghi=555, jkl=456, mno=567, pqr=678, stu=789, vwx=890, yza=901}
1000 | {abc=923, def=634, ghi=666, jkl=456, mno=567, pqr=678, stu=789, vwx=890, yza=901}
1000 | {abc=923, def=434, ghi=777, jkl=456, mno=567, pqr=678, stu=789, vwx=890, yza=901}
1000 | {abc=923, def=434, ghi=888, jkl=456, mno=567, pqr=678, stu=789, vwx=890, yza=901}
1000 | {abc=923, def=234, ghi=999, jkl=456, mno=567, pqr=678, stu=789, vwx=890, yza=901}
1000 | {abc=923, def=234, ghi=000, jkl=456, mno=567, pqr=678, stu=789, vwx=890, yza=901}
There are other columns, just not shown. The id column can have different values, but in this example, does not. In the data column, only the fields abc, def, and ghi differ, all the others are the same. Again this is only illustrative for this data example.
I have written a function to extract the value assigned to fields in the data column, and it is used in the following query:
select id
,extract_data(data,abc) as abc
,extract_data(data,def) as def
from table
giving results:
id | abc | def
1000 | 123 | 234
1000 | 123 | 234
1000 | 123 | 434
1000 | 123 | 434
1000 | 123 | 634
1000 | 923 | 634
1000 | 923 | 434
1000 | 923 | 434
1000 | 923 | 234
1000 | 923 | 234
For reporting purposes, I would like to be able to display the amount of each type of record. There are 6 types in the above example, and ideally the output would be:
id | abc | def | count
1000 | 123 | 234 | 2
1000 | 123 | 434 | 2
1000 | 123 | 634 | 1
1000 | 923 | 634 | 1
1000 | 923 | 434 | 2
1000 | 923 | 234 | 2
I expected to achieve this by writing SQL like so (and I'm convinced I have done so in the past):
select id
,extract_data(data,abc) as abc
,extract_data(data,def) as def
,count(1)
from table
group by id
,abc
,def
This however, will not work. Oracle is giving me an error of:
ORA-00904: "ABC": invalid identifier
00904. 00000 - "%s: invalid identifier"
From my initial research on "the google", I have seen that I should perhaps be grouping on the column I am passing into my user defined function. This would be due to SQL requiring all columns not part of an aggregate function needing to be part of the group by clause.
This will work for some records, however in my data example, the field ghi in the data column is different for every record , thus making the data column unique, and ruining the group by clause, as a count of 1 is given for each record.
I've used sybase and db2 in the past, and (setting myself up for a fall here...) I'm pretty sure in both that I was able to group by on the output of a user defined function.
I thought that there might be an issue with the naming of the columns and how they can be referenced by the group by? Referencing by column number hasn't worked.
I've tried various combinations of what I have, and can't get it to work, so I'd appreciate any insight you guys out there could give.
If you need any more information I'll edit as required or clarify in the comments.
Thanks,
GC.
You should be able to group by the functions themselves, not by the aliases
select id
,extract_data(data,abc) as abc
,extract_data(data,def) as def
,count(*)
from table
group by id
,extract_data(data,abc)
,extract_data(data,def)
Note that this does not generally involve executing the function multiple times. You can see that yourself with a simple function that increments a counter in a package every time it is called
SQL> ed
Wrote file afiedt.buf
1 create or replace package pkg_counter
2 as
3 g_cnt integer := 0;
4* end;
SQL> /
Package created.
SQL> create or replace function f1( p_arg in number )
2 return number
3 is
4 begin
5 pkg_counter.g_cnt := pkg_counter.g_cnt + 1;
6 return mod( p_arg, 2 );
7 end;
8 /
Function created.
There are 16 rows in the EMP table
SQL> select count(*) from emp;
COUNT(*)
----------
16
so when we execute a query that involves grouping by the function call, we hope to see the function executed only 16 times. And that is, in fact, what we see.
SQL> select deptno,
2 f1( empno ),
3 count(*)
4 from emp
5 group by deptno,
6 f1( empno );
DEPTNO F1(EMPNO) COUNT(*)
---------- ---------- ----------
1 1
30 0 4
20 1 1
10 0 2
30 1 2
20 0 4
10 1 1
0 1
8 rows selected.
SQL> begin
2 dbms_output.put_line( pkg_counter.g_cnt );
3 end;
4 /
16
PL/SQL procedure successfully completed.
Try this:
select id, abc, def, count(1)
from
(
select
id,
extract_data(data,abc) as abc,
extract_data(data,def) as def
from table
)
group by id, abc, def
Have you tried:
SELECT
id,
extract_data(data, abc) as abc,
extract_data(data, def) as def,
COUNT(1)
FROM
table
GROUP BY
id,
extract_data(data, abc)
extract_data(data, def)

a Rollup query with some logical netting using Oracle SQL

I have a table "AuctionResults" like below
Auction Action Shares ProfitperShare
-------------------------------------------
Round1 BUY 6 200
Round2 BUY 5 100
Round2 SELL -2 50
Round3 SELL -5 80
Now I need to aggregate results by every auction with BUYS after netting out SELLS in subsequent rounds on a "First Come First Net basis"
so in Round1 I bought 6 Shares and then sold 2 in Round2 and rest "4" in Round3 with a total NET profit of 6 * 200-2 * 50-4 * 80 = 780
and in Round2 I bought 5 shares and sold "1" in Round3(because earlier "4" belonged to Round1) with a NET Profit of 5 * 100-1 * 80 = 420
...so the Resulting Output should look like:
Auction NetProfit
------------------
Round1 780
Round2 420
Can we do this using just Oracle SQL(10g) and not PL-SQL
Thanks in advance
I know this is an old question and won't be of use to the original poster, but I wanted to take a stab at this because it was an interesting question. I didn't test it out enough, so I would expect this still needs to be corrected and tuned. But I believe the approach is legitimate. I would not recommend using a query like this in a product because it would be difficult to maintain or understand (and I don't believe this is really scalable). You would be much better off creating some alternate data structures. Having said that, this is what I ran in Postgresql 9.1:
WITH x AS (
SELECT round, action
,ABS(shares) AS shares
,profitpershare
,COALESCE( SUM(shares) OVER(ORDER BY round, action
ROWS BETWEEN UNBOUNDED PRECEDING
AND 1 PRECEDING)
, 0) AS previous_net_shares
,COALESCE( ABS( SUM(CASE WHEN action = 'SELL' THEN shares ELSE 0 END)
OVER(ORDER BY round, action
ROWS BETWEEN UNBOUNDED PRECEDING
AND 1 PRECEDING) ), 0 ) AS previous_sells
FROM AuctionResults
ORDER BY 1,2
)
SELECT round, shares * profitpershare - deduction AS net
FROM (
SELECT buy.round, buy.shares, buy.profitpershare
,SUM( LEAST( LEAST( sell.shares, GREATEST(buy.shares - (sell.previous_sells - buy.previous_sells), 0)
,GREATEST(sell.shares + (sell.previous_sells - buy.previous_sells) - buy.previous_net_shares, 0)
)
) * sell.profitpershare ) AS deduction
FROM x buy
,x sell
WHERE sell.round > buy.round
AND buy.action = 'BUY'
AND sell.action = 'SELL'
GROUP BY buy.round, buy.shares, buy.profitpershare
) AS y
And the result:
round | net
-------+-----
1 | 780
2 | 420
(2 rows)
To break it down into pieces, I started with this data set:
CREATE TABLE AuctionResults( round int, action varchar(4), shares int, profitpershare int);
INSERT INTO AuctionResults VALUES(1, 'BUY', 6, 200);
INSERT INTO AuctionResults VALUES(2, 'BUY', 5, 100);
INSERT INTO AuctionResults VALUES(2, 'SELL',-2, 50);
INSERT INTO AuctionResults VALUES(3, 'SELL',-5, 80);
INSERT INTO AuctionResults VALUES(4, 'SELL', -4, 150);
select * from auctionresults;
round | action | shares | profitpershare
-------+--------+--------+----------------
1 | BUY | 6 | 200
2 | BUY | 5 | 100
2 | SELL | -2 | 50
3 | SELL | -5 | 80
4 | SELL | -4 | 150
(5 rows)
The query in the "WITH" clause adds some running totals to the table.
"previous_net_shares" indicates how many shares are available to sell before the current record. This also tells me how many 'SELL' shares I need to skip before I can start allocating it to this 'BUY'.
"previous_sells" is a running count of the number of "SELL" shares encountered, so the difference between two "previous_sells" indicates the number of 'SELL' shares used in that time.
round | action | shares | profitpershare | previous_net_shares | previous_sells
-------+--------+--------+----------------+---------------------+----------------
1 | BUY | 6 | 200 | 0 | 0
2 | BUY | 5 | 100 | 6 | 0
2 | SELL | 2 | 50 | 11 | 0
3 | SELL | 5 | 80 | 9 | 2
4 | SELL | 4 | 150 | 4 | 7
(5 rows)
With this table, we can do a self-join where each "BUY" record is associated with each future "SELL" record. The result would look like this:
SELECT buy.round, buy.shares, buy.profitpershare
,sell.round AS sellRound, sell.shares AS sellShares, sell.profitpershare AS sellProfitpershare
FROM x buy
,x sell
WHERE sell.round > buy.round
AND buy.action = 'BUY'
AND sell.action = 'SELL'
round | shares | profitpershare | sellround | sellshares | sellprofitpershare
-------+--------+----------------+-----------+------------+--------------------
1 | 6 | 200 | 2 | 2 | 50
1 | 6 | 200 | 3 | 5 | 80
1 | 6 | 200 | 4 | 4 | 150
2 | 5 | 100 | 3 | 5 | 80
2 | 5 | 100 | 4 | 4 | 150
(5 rows)
And then comes the crazy part that tries to calculate the number of shares available to sell in the order vs the number over share not yet sold yet for a buy. Here are some notes to help follow that. The "greatest"calls with "0" are just saying we can't allocate any shares if we are in the negative.
-- allocated sells
sell.previous_sells - buy.previous_sells
-- shares yet to sell for this buy, if < 0 then 0
GREATEST(buy.shares - (sell.previous_sells - buy.previous_sells), 0)
-- number of sell shares that need to be skipped
buy.previous_net_shares
Thanks to David for his assistance