Selecting independent rows and displaying them into a single row (ORACLE SQL) - sql

I have a table called requesttool.request_detail which is used to store attributes for entities identified by the value in column REQUEST_ID. The table requesttool.request_detail has a column called ATTRIBUTE_ID which indicates what is stored in the respective row of another column called ATTRIBUTE_VALUE. For instance, if ATTRIBUTE_ID='259' for a given row then name will be stored in that respective row of ATTRIBUTE_VALUE.
Here is what requesttool.request_detail looks like in practice:
What I want to do is to extract the value stored in ATTRIBUTE_VALUE for 3 different ATTRIBUTE_ID's and for a given REQUEST_ID, say 4500161635, and display them in a single row, like this:
I have tried the following code:
select
request_id,
case when attribute_id = '289' then attribute_value end as name,
case when attribute_id = '259' then attribute_value end as country,
case when attribute_id = '351' then attribute_value end as priority
from (
select a.request_id, a.attribute_id, a.attribute_value
from requesttool.request_detail a
where a.request_id='4500161635');
but from this I obtain a table with null values, not a single line:

You are on the right track. Only you'd have to aggregate your rows so as to get one result row per request_id:
select
request_id,
max(case when attribute_id = '289' then attribute_value end) as name,
max(case when attribute_id = '259' then attribute_value end) as country,
max(case when attribute_id = '351' then attribute_value end) as priority
from requesttool.request_detail
where request_id = '4500161635'
group by request_id;
Given an index on request_id + attribute_id, you might be able to speed this up by adding a condition to your where clause:
and attribute_id in ('289', '259', '351')
BTW: Are request_id and attribute_id really strings or why are you using quotes on the numbers?

Try this
select
request_id,
MIN(case when attribute_id = '289' then attribute_value end) as name,
MIN(case when attribute_id = '259' then attribute_value end) as country,
MIN(case when attribute_id = '351' then attribute_value end) as priority
from (
select a.request_id, a.attribute_id, a.attribute_value
from requesttool.request_detail a
where a.request_id='4500161635')
GROUP BY request_id

Related

Is there a way to make this run without a case statement? [duplicate]

This question already has answers here:
TSQL Pivot without aggregate function
(9 answers)
Closed 1 year ago.
I'm relatively new to coding and SQL so please bear with me.
I'm currently working on a query and I have no idea how to get the infinite loop to stop without using a case statement. When I use the case statement I get each value on its own row rather than the values all together in the combination they're supposed to be in.
Case statement SQL
select
CASE
When Attribute_id = '5024923' Then attribute_value
END Page_Name,
CASE
When Attribute_id = '5024925' Then attribute_value
END Site_Name,
CASE
When Attribute_id = '5024924' Then attribute_value
END Last_Touch_Channel,
count(distinct MASTER_CONTACT_ID) known_contact_count,
count (distinct visitor_id) total_contact_Count,
ACTION_DATE
from Adobe_Analytics_Staging
where ATTRIBUTE_ID in ('5024925','5024924','5024923')
group by ATTRIBUTE_ID, ACTION_DATE, ATTRIBUTE_VALUE
Example:
Error with Case statement:
Column A
Column B
Column C
value1
NULL
NULL
NULL
value2
NULL
NULL
NULL
value3
When in the data it is value1, value2, value3 on the same row.
So I'm trying a new avenue. I suspect the loop is because I'm linking back to the table so many times but I have limited the amount of results to the best of my ability to reduce the amount of records being sent through. Each query works and works fast individually. It's collectively that it slows down a ton.
The reason for joining to the table so many times is because I have to distinguish different types of values within one column.
Note: Not sure if it's relevant but the different values in the table correlate to a specific id number within that that table. Attribute value and attribute ID are different columns
For example in Table A the column looks like this
Column
A
B
C
I have to make it look like this:
Column 1
Column 2
Column 3
A
B
C
select
a.ATTRIBUTE_VALUE,
b.ATTRIBUTE_VALUE,
c.ATTRIBUTE_VALUE,
count(distinct aas.MASTER_CONTACT_ID) known_contact_count,
count (distinct d.visitor_id) total_contact_Count,
aas.ACTION_DATE
from Adobe_Analytics_Staging aas
left join (select ATTRIBUTE_VALUE, VISITOR_ID from Adobe_Analytics_Staging
where Attribute_id = '5024923') a on a.VISITOR_ID = aas.VISITOR_ID
left join (select ATTRIBUTE_VALUE, VISITOR_ID from Adobe_Analytics_Staging
where Attribute_id = '5024925') b on b.VISITOR_ID = aas.VISITOR_ID
left join (select ATTRIBUTE_VALUE, VISITOR_ID from Adobe_Analytics_Staging
where Attribute_id = '5024924') c on c.VISITOR_ID = aas.VISITOR_ID
inner join (select visitor_id from Adobe_Analytics_Staging
where ATTRIBUTE_ID in ('5024923','5024925','5024924')) d
on d.VISITOR_ID = aas.VISITOR_ID
--where aas.VISITOR_ID = '3438634761938550664_6795123974460253552'
group by a.ATTRIBUTE_VALUE, b.ATTRIBUTE_VALUE, c.ATTRIBUTE_VALUE, aas.ACTION_DATE
SELECT
VISITOR_ID,
MAX(CASE WHEN Attribute_id = '5024923' Then attribute_value END) Page_Name,
MAX(CASE WHEN Attribute_id = '5024925' Then attribute_value END) Site_Name,
MAX(CASE WHEN Attribute_id = '5024924' Then attribute_value END) Last_Touch_Channel,
COUNT(distinct MASTER_CONTACT_ID) known_contact_count,
COUNT(distinct visitor_id) total_contact_Count,
ACTION_DATE
FROM ContactTargeting.dbo.Adobe_Analytics_Staging
GROUP BY VISITOR_ID, ACTION_DATE
See this fiddle with some demo data

flatten data in SQL based on fixed set of column

I am stuck with a specific scenario of flattening the data and need help for it. I need the output as flattened data where the column values are not fixed. Due to this I want to restrict the output to fixed set of columns.
Given Table 'test_table'
ID
Name
Property
1
C1
xxx
2
C2
xyz
2
C3
zz
The scenario is, column Name can have any no. of values corresponding to an ID. I need to flatten the data based in such a way that there is one row per ID field. Since the Name field varies with each ID, I want to flatten it for fix 3 columns like Co1, Co2, Co3. The output should look like
ID
Co1
Co1_Property
Co2
Co2_Property
Co3
Co3_Property
1
C1
xxx
null
null
2
C2
xyz
C3
zz
Could not think of a solution using Pivot or aggregation. Any help would be appreciated.
You can use arrays:
select id,
array_agg(name order by name)[safe_ordinal(1)] as name_1,
array_agg(property order by name)[safe_ordinal(1)] as property_1,
array_agg(name order by name)[safe_ordinal(2)] as name_2,
array_agg(property order by name)[safe_ordinal(2)] as property_2,
array_agg(name order by name)[safe_ordinal(3)] as name_3,
array_agg(property order by name)[safe_ordinal(3)] as property_3
from t
group by id;
All current answers are too verbose and involve heavy repetition of same fragments of code again and again and if you need to account more columns you need to copy paste and add more lines which will make it even more verbose!
My preference is to avoid such type of coding and rather use something more generic as in below example
select * from (
select *, row_number() over(partition by id) col
from `project.dataset.table`)
pivot (max(name) as name, max(property) as property for col in (1, 2, 3))
If applied to sample data in your question - output is
If you want to change number of output columns - you just simply modify for col in (1, 2, 3) part of query.
For example if you would wanted to have 5 columns - you would use for col in (1, 2, 3, 4, 5) - that simple!!!
The standard practice is to use conditional aggregation. That is, to use CASE expressions to pick which row goes to which column, then MAX() to collapse multiple rows into individual rows...
SELECT
id,
MAX(CASE WHEN name = 'C1' THEN name END) AS co1,
MAX(CASE WHEN name = 'C1' THEN property END) AS co1_property,
MAX(CASE WHEN name = 'C2' THEN name END) AS co2,
MAX(CASE WHEN name = 'C2' THEN property END) AS co2_property,
MAX(CASE WHEN name = 'C3' THEN name END) AS co3,
MAX(CASE WHEN name = 'C3' THEN property END) AS co3_property
FROM
yourTable
GROUP BY
id
Background info:
Not having an ELSE in the CASE expression implicitly means ELSE NULL
The intention is therefore for each column to recieve NULL from every input row, except for the row being pivoted into that column
Aggregates, such as MAX() essentially skip NULL values
MAX( {NULL,NULL,'xxx',NULL,NULL} ) therefore equals 'xxx'
A similar approach "bunches" the values to the left (so that NULL values always only appears to the right...)
That approach first uses row_number() to give each row a value corresponding to which column you want to put that row in to..
WITH
sorted AS
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY name) AS seq_num
FROM
yourTable
)
SELECT
id,
MAX(CASE WHEN seq_num = 1 THEN name END) AS co1,
MAX(CASE WHEN seq_num = 1 THEN property END) AS co1_property,
MAX(CASE WHEN seq_num = 2 THEN name END) AS co2,
MAX(CASE WHEN seq_num = 2 THEN property END) AS co2_property,
MAX(CASE WHEN seq_num = 3 THEN name END) AS co3,
MAX(CASE WHEN seq_num = 3 THEN property END) AS co3_property
FROM
yourTable
GROUP BY
id

BigQuery(standard SQL) grouping values based on first CASE WHEN statement

Here is my query with the output below the syntax.
SELECT DISTINCT CASE WHEN id = 'RUS0261431' THEN value END AS sr_type,
COUNT(CASE WHEN id in ('RUS0290788') AND value in ('1','2','3','4') THEN respondentid END) AS sub_ces,
COUNT(CASE WHEN id IN ('RUS0290788') AND value in ('5','6','7') THEN respondentid END) AS pos_ces,
COUNT(*) as total_ces
FROM `some_table`
WHERE id in ( 'RUS0261431') AND id <> '' AND value IS NOT NULL
GROUP BY 1
As you can see with the attached table I'm unable to group the values based on Id RUS0290788 with the distinct values that map to RUS0261431. Is there anyway to pivot with altering my case when statements so I can group sub_ces and pos_ces by sr_type. Thanks in advanceenter image description here
You can simplify your WHERE condition to WHERE id = ('RUS0261431'). Only records with this value will be selected so you do not have to repeat this in the CASE statements.

GROUP BY a column in the same table to two alias columns

I have this table:
table
And I want to know if there is some SQL query to return something like this:
result
I tried this but didn't work:
SELECT Object,
SUM(CASE WHEN Key = 'A' THEN Qty END) As Key A,
SUM(CASE WHEN Key = 'B' THEN Qty END) As Key B
FROM tab
And even added the GROUP BY clause but the error is at the CASE clause
I would expect something like this:
SELECT Object,
SUM(CASE WHEN Key = 'A' THEN Qty END) As KeyA,
SUM(CASE WHEN Key = 'B' THEN Qty END) As KeyB
FROM table -- table won't work as a table name
GROUP BY object;
I added the GROUP BY and fixed the column aliases.
add group by in your query as you execute the aggregation
SELECT Object,
SUM(CASE WHEN Key = 'A' THEN Qty END) As Key_A,
SUM(CASE WHEN Key = 'B' THEN Qty END) As Key_B
FROM table group by Object
you have used space of column alias name i changed it

Index - Match like function in PL-SQL

Recently I have very specific problem with data we get from our data-warehouse. The problem is being solved, but I have to edit our control environment for a while.
We have data about received invoices, however due to some reason, information about every invoice is split into two rows: First row has important columns unique_code_A, vendor_number, and the second row has important columns unique_code_B, amount. So every invoice has very specific unique code, and with this code I have to somehow join the information from both rows, as you can see in picture.
Well, you can use aggregation:
select date_key, invoice_type,
max(case when unique_code_b is null then unique_code_a end) as unique_code_a,
max(unique_code_b) as unique_code_b,
max(case when unique_code_b is null then vendor_number end) as vendor_number,
max(case when unique_code_b is not null then amount end) as amount
from t
group by date_key, invoice_type;
EDIT:
If the unique codes can be used for matching, then I would suggest:
select date_key, invoice_type,
coalesce(unique_code_a, unique_code_b) as unique_code,
max(case when unique_code_b is null then vendor_number end) as vendor_number,
max(case when unique_code_b is not null then amount end) as amount
from t
group by date_key, invoice_type, coalesce(unique_code_a, unique_code_b);
From what you told, a self join should probably work:
SELECT
A.DATE_KEY,
A.INVOICE_TYPE,
A.UNIQUE_CODE_A,
B.UNIQUE_CODE_B,
A.VENDOR_NUMBER,
B.AMOUNT
FROM MyTable A
INNER JOIN MyTable B ON A.UNIQUE_CODE_A=B.UNIQUE_CODE_B