Build multi columns as pivot in Oracle - sql

I am trying pivot the data based on existing data. I have details as below.
based on strt_dt, I need to do pivot up to 36 months. I cannot add 36 cols as pivoting by using min or max functions. Please advice me what is the better way. I need to pivot 36 cols for 36 months.
Like strt_dt value is 202003 then 202004,202005,202006....202305.
Table sample data:
Thanks for your help in advance.

Why don't you build a dynamic query.
e.g. first build the columns :
select listagg('SUM(CASE WHEN to_char(start_dt,''YYYYMM'')='
||sdt||' THEN PRC_AMT END) as prc_amt'
||sdt,',') WITHIN GROUP (ORDER BY sdt)
into colList
from (select distinct to_char(start_dt,'YYYYMM') sdt
from price_details)
then generate the actual query :
lv_sql := 'select '||colList||'
from price_details
group by to_char(start_dt,''YYYYMM'')';
and execute it any way you want :
execute immediate lv_sql into your_variables;

Related

How can I use a row value to dynamically select a column name in Oracle SQL 11g?

I have two tables, one with a single row for each "batch_number" and another with defect details for each batch. The first table has a "defect_of_interest" column which I would like to link to one of the columns in the second table. I am trying to write a query that would then pick the maximum value in that dynamically linked column for any "unit_number" in the "batch_number".
Here is the SQLFiddle with example data for each table: http://sqlfiddle.com/#!9/a1c27d
For example, the maximum value in the DEFECT_DETAILS.SCRATCHES column for BATCH_NUMBER = A1 is 12.
Here is my desired output:
BATCH_NUMBER DEFECT_OF_INTEREST MAXIMUM_DEFECT_COUNT
------------ ------------------ --------------------
A1 SCRATCHES 12
B3 BUMPS 4
C2 STAINS 9
I have tried using the PIVOT function, but I can't get it to work. Not sure if it works in cases like this. Any help would be much appreciated.
If the number of columns is fixed (it seems to be) you can use CASE to select the specific value according to the related table. Then aggregating is simple.
For example:
select
batch_number,
max(defect_of_interest) as defect_of_interest,
max(defect_count) as maximum_defect_count
from (
select
d.batch_number,
b.defect_of_interest,
case when b.defect_of_interest = 'SCRATCHES' then d.scratches
when b.defect_of_interest = 'BUMPS' then d.bumps
when b.defect_of_interest = 'STAINS' then d.stains
end as defect_count
from defect_details d
join batches b on b.batch_number = d.batch_number
) x
group by batch_number
order by batch_number;
See Oracle example in db<>fiddle.

SQL: Select Top 2 Query is Excluding Records with more than 2 Records

I just joined after having a problem writing a query in MS Access. I am trying to write a query that will pull out the first two valid samples in from a list of replicated sample results and then would like to average the sample values. I have written a query that does pull samples with only two valid samples and averages these values. However, my query doesn't pull samples where there are more than two valid sample results. Here's my query:
SELECT temp_platevalid_table.samp_name AS samp_name, avg (temp_platevalid_table.mean_conc) AS fin_avg, count(temp_platevalid_table.samp_valid) AS sample_count
FROM Temp_PlateValid_table
WHERE (Temp_PlateValid_table.id In (SELECT TOP 2 S.id
FROM Temp_PlateValid_table as S
WHERE S.samp_name = S.samp_name and s.samp_valid=1 and S.samp_valid=1
ORDER BY ID))
GROUP BY Temp_PlateValid_table.samp_name
HAVING ((Count(Temp_PlateValid_table.samp_valid))=2)
ORDER BY Temp_PlateValid_table.samp_name;
Here's an example of what I'm trying to do:
ID Samp_Name Samp_Valid Mean_Conc
1 54d2d2 1 15
2 54d2d2 1 20
3 54d2d2 1 25
The average mean_conc should be 17.5, however, with my current query, I wouldn't receive a value at all for 54d2d2. Is there a way to tweak my query so that I get a value for samples that have more than two valid values? Please note that I'm using MS Access, so I don't think I can use fancier SQL code (partition by, etc.).
Thanks in advance for your help!
Is this what you want?
select pv.samp_name, avg(pv.value_conc)
from Temp_PlateValid_table pv
where pv.samp_valid = 1 and
pv.id in (select top 2 id
from Temp_PlateValid_table as pv2
where pv2.samp_name = pv.samp_name and pv2.samp_valid = 1
)
group by pv.samp_name;
You might need avg(pv.value_conc * 1.0).

SQL Nested Aggregation

I am working with SQL Language.
I have a table named parta. I want to count the fields 40b1 and 40b2 and find the sum of this. My query is here.
select
count(40b1) as 40b1,
count(40b2) as 40b2,
sum(count(40b1) + count(40b2) ) as sum,
code/100 as code
from parta
where 40b1=true and mandays>=1000
group by code/100 ;
Expected output
40b1 40b2 sum code verticalsum
5 5 10 20 7
2 2 4 21 7
How it done? Please help.
For getting this verticalsum column, what query can I use?
You don't need to SUM() the COUNT()'s. Just add them together.
select count(40b1) as 40b1,
count(40b2) as 40b2,
count(40b1) + count(40b2) as sum,
code/100 as code
from parta
where 40b1=true and mandays>=1000
group by code/100 ;

Adding a percent column to MS Access Query

I'm trying to add a column which calculates percentages of different products in MS Access Query. Basically, this is the structure of the query that I'm trying to reach:
Product |
Total |
Percentage
Prod1 |
15 |
21.13%
Prod2 |
23 |
32.39%
Prod3 |
33 |
46.48%
Product |
71 |
100%
The formula for finding the percent I use is: ([Total Q of a Product]/[Totals of all Products])*100, but when I try to use the expression builder (since my SQL skills are basic) in MS Access to calculate it..
= [CountOfProcuts] / Sum([CountOfProducts])
..I receive an error message "Cannot have aggregate function in GROUP BY clause.. (and the expression goes here)". I also tried the option with two queries: one that calculates only the totals and another that use the first one to calculate the percentages, but the result was the same.
I'll be grateful if someone can help me with this.
You can get all but the last row of your desired output with this query.
SELECT
y.Product,
y.Total,
Format((y.Total/sub.SumOfTotal),'#.##%') AS Percentage
FROM
YourTable AS y,
(
SELECT Sum(Total) AS SumOfTotal
FROM YourTable
) AS sub;
Since that query does not include a JOIN or WHERE condition, it returns a cross join between the table and the single row of the subquery.
If you need the last row from your question example, you can UNION the query with another which returns the fabricated row you want. In this example, I used a custom Dual table which is designed to always contain one and only one row. But you could substitute another table or query which returns a single row.
SELECT
y.Product,
y.Total,
Format((y.Total/sub.SumOfTotal),'#.##%') AS Percentage
FROM
YourTable AS y,
(
SELECT Sum(Total) AS SumOfTotal
FROM YourTable
) AS sub
UNION ALL
SELECT
'Product',
DSum('Total', 'YourTable'),
'100%'
FROM Dual;

Increment Days in SQL query

I am trying to write a query that will output the following table:
|Day_0_Revenue|Day_1_Revenue|Day_2_Revenue|....|Day_90_Revenue|
The data looks like the following table. Each day will have multiple values, and I want to sum the revenue for each day. Day 0 is 11/1.
|Date|Revenue|
11/1 5
11/2 3
11/3 5
11/3 7
11/4 8
11/5 8
11/5 12
11/6 7
I believe this is simple to do if the table I want is vertical. The reason I want to output horizontally is because I will fill the table vertically with other dates. My main question is how to increment the day value without writing a really long SELECT clause? I'm not sure if I can write a loop that will have something like Day+1 until Day=90...
This is not possible in SQLite alone.
The easiest way would be to just use GROUP BY day to create a vertically oriented table, and then reorder the cells when you are displaying them.
If you really must get a horizontally oriented table from the database, you have to create the query dynamically:
SELECT (SELECT SUM(Revenue) FROM MyTable WHERE Date = '11/1') AS Day_0_Revenue,
(SELECT SUM(Revenue) FROM MyTable WHERE Date = '11/2') AS Day_1_Revenue,
(SELECT SUM(Revenue) FROM MyTable WHERE Date = '11/3') AS Day_2_Revenue,
...
You have to do this in whatever programming language you are using to access the database:
sql = "SELECT"
for i in range(90):
sql += " (SELECT SUM(Revenue) FROM MyTable" +
" WHERE Date = '%s') AS Day_%d_Revenue," % (dates[i], i)
sql = sql[:-1] # remove last comma
cursor = db.execute(sql)