How to transform rows into different columns in BigQuery? [duplicate] - google-bigquery

This question already has answers here:
Convert rows to columns in Bigquery
(2 answers)
Closed 12 days ago.
For short, I have this nested fields that I'd like to separate into different columns.
Here's my table now:
transaction_id
transaction_name
transaction_amount
123
item_1
200
123
item_2
100
124
item_1
200
124
item_2
300
124
item_3
400
And here's the output that I'd like to have
transaction_id
transaction_name_1
transaction_amount_1
transaction_name_2
transaction_amount_2
transaction_name_3
transaction_amount_3
123
item_1
200
item_2
100
NULL
NULL
124
item_1
200
item_2
300
item_3
400
Does anyone has the solution for this? Thanks in advance

You can use PIVOT query :
SELECT * FROM sample_table
PIVOT (
ANY_VALUE(transaction_name) transaction_name,
ANY_VALUE(transaction_amount) transaction_amount
FOR transaction_name IN ('item_1' `1`, 'item_2' `2`, 'item_3' `3`)
);
Query results
Using a dynamic sql:
EXECUTE IMMEDIATE(FORMAT("""
SELECT * FROM sample_table
PIVOT (
ANY_VALUE(transaction_name) transaction_name,
ANY_VALUE(transaction_amount) transaction_amount
FOR transaction_name IN ('%s')
);""", (SELECT STRING_AGG(DISTINCT transaction_name, "','") FROM sample_table))
);

Related

How to get the last day of the month without LAST_DAY() or EOMONTH()?

I have a table t with:
DATE
LOCATION
PRODUCT_ID
AMOUNT
2021-10-29
1
123
10
2021-10-30
1
123
9
2021-10-31
1
123
8
2021-10-29
1
456
100
2021-10-30
1
456
90
2021-10-31
1
456
80
2021-10-29
2
123
18
2021-10-30
2
123
17
2021-11-29
2
456
18
I need to find the AMOUNT of each PRODUCT_ID for each combination of LOCATION + PRODUCT_ID.
If a PRODUCT_ID has no entry for that day the AMOUNT is NULL.
So the result should look like:
DATE
LOCATION
PRODUCT_ID
AMOUNT
2021-10-31
1
123
8
2021-10-31
1
456
80
2021-10-31
2
123
NULL
2021-11-30
2
456
NULL
Sadly EXASOL has no LAST_DAY() or EOMONTH() function. How can I solve this?
You can get to the last day of the month using a date_trunc function in combination with date_add:
case
when t.date = date_add('day', -1, date_add('month', 1, date_trunc('month', t.date)))
then 'Y' else 'N' end as end_of_month
That being said, if you group your table for all combinations of locations and products, you will not get NULLs for products without sales on the last day of the month as shown in your output table.
When you group your data, any value that does not exist will simply not show up in your output table. If you want to force nulls to show up, you can create a new table that contains all combinations of products, locations, and hard-coded end of month dates.
Then, you can left join your old table with this new hard-coded table by date, location, and product. This method will give you the NULL values you expect.

SQL Insert two rows in other table based on If confition

I have 2 table which is DATA and MAIN. DATA table is the raw data extract from excel and MAIN table is the data after validation(few rules) have been made.
Rule:
If Amoun1<>'' and Amount2<>''.
Insert 2 row in MAIN table. The first row will have the value for Amount and Percentage from Amount2 & Percentage2 with TaxRateType = Taxable.
And the second row will have Amount and Percentage get from Amount1 & Percentage1 with TaxRateType = EXEMPT. The invoiceNo also will be add with '_1'
If Amount2<>'' and Amount1=''
Insert 1 row with Amount and Percentage from Amount2 & Percentage2 with TaxRateType = Taxable.
Else
Insert 1 row with Amount and Percentage get from Amount1 & Percentage1 with TaxRateType = EXEMPT
The example is as below table:
**DATA**
InvoiceNo | TotalAmount | Percentage1 | Amount1 | Percentage2 | Amount2
abc123 100 5 45 20 55
abc124 60 5 60 20
abc125 50 5 22 50
**MAIN**
InvoiceNo | Percentage | Amount | TaxRateType | ReferenceValue
abc123 20 55 TAXABLE 2
abc123_1 5 45 EXEMPT 1
abc124 5 60 EXEMPT 1
abc125 22 50 TAXABLE 2
I'm stuck in here for 4 hours searching for which method to use. Currently I have an idea to use if exists but still it's not correct and somehow I feel its not a good method.
IF EXISTS (SELECT ID from [alcatel].[Main_Temp] where Amount0<>'' and Amount21<>'')
BEGIN
INSERT INTO [alcatel].[Main]
( [Country],[InvoiceNo],[Amount],[Percentage],[TaxRateType],[Reference Value])
SELECT
[Country],[InvoiceNo],[Amount2],[Percentage2],'TAXABLE' as [TaxRateType],2 as [Reference Value]
FROM [alcatel].[Data];
INSERT INTO [alcatel].[Main]
( [Country],[InvoiceNo],[Amount],[Percentage],[TaxRateType],[Reference Value])
SELECT
[Country],[InvoiceNo]+'_1' as InvoiceNo,[Amount1],[Percentage1],'EXEMPT' as [TaxRateType],'1' as [Reference Value]
FROM [alcatel].[Data];
END
Followed with other condition.
I think you just want to unpivot the data with some logic:
select invoiceno + v.suffix, v.percentage, v.amount,
v.taxratetype, v.referencevalue
from data d cross apply
(values (1, d.Percentage1, d.Amount1, 'EXEMPT', ''),
(2, d.Percentage2, d.Amount2, 'TAXABLE', (case when d.amount1 is not null then '_1' else '' end))
) v(ReferenceValue, Percentage, Amount, TaxRateType, Suffix)
where amount is not null;
Here is a db<>fiddle.

Postgres: Convert single rows from multiple tables into multiple rows in a single table

I have data scattered across multiple tables and rows that I'd like to aggregate into a more usable format for my use case. The problem boils down to something like this...
If I have two tables like this:
product_id title_id description_id
1 123 234
2 345 456
3 567 678
product_id additional_info_id
1 789
1 890
2 901
How would I construct a query to return data like this?
product_id content_id content_type
1 123 title
1 234 description
1 789 additional_info
1 890 additional_info
2 345 title
2 456 description
2 901 additional_info
3 567 title
3 678 Description
I found this post and I can construct a query like this
select
p.product_id,
p_content.*
from
product p,
lateral (values
(p.title_id, 'title'),
(p.description_id, 'description')
) p_content (content_id, content_type)
;
to get the data from the product table in the format I need, but I can't figure out how to incorporate the data from the additional_info table.
You can use union all:
select p.product_id, v.content_id, v.content_type
from product p cross join lateral
(values (p.title_id, 'title'),
(p.description_id, 'description')
) v(content_id, content_type)
union all
select product_id, additional_info_id, 'additional_info'
from additional_info ai;

SQL retrieval from a table

Sales table
ItemID Date Code
-------------------------------------------------
12345 2019-02-17 1
12345 2019-02-17 2
12345 2019-02-17 3
-------------------------------------------------
12344 2019-02-18 1
12344 2019-02-18 3
12344 2019-02-18 2
-------------------------------------------------
12443 2019-02-19 1
12443 2019-02-19 2
12443 2019-02-19 3
I want to retrieve those item ids from Sales table where Code 2 is updated after code 3. So my query should return 12344 as result... I cannot think of any functions to achieve this as I have limited knowledge on SQL. Can someone please provide me some suggestion on achieving this? Thanks
Here is the query that you are looking for
select sales1.itemId from sales sales1
inner join sales sales2
on sales1.itemId = sales2.itemId
where sales1.date > sales2.date and sales1.code = 2 and sales2.code = 3;
Check if it helps you.
You can try using lag()
select itemid from
(
select sales1.itemId,sales1.code,
sales1.code-lag(code) over(partition by itemid order by dates) as diff
from sales sales1
where code in (2,3)
)A where diff>=1
You can include a column
update_seq_no
Each time you update you can increase the update_seq_no and then you can achieve your requirement you want using the below sql.
alter table sales add (update_seq_no number);
create SEQUENCE IFSQ_UPDATE_SEQNO MINVALUE 1000 MAXVALUE 9999999999999999 INCREMENT BY 1 START WITH 1 NOCYCLE ;
-- for existing rows (just a work around)
BEGIN
for rec in (select rowid row_id,itemid,date from sales where update_Seq_no is null order by 2,3 asc)
loop
update sales set update_Seq_no=IFSQ_UPDATE_SEQNO.NEXTVAL
where rowid=rec.row_id;
commit;
end loop;
end;
--for new rows
insert into sales values(p_itemid,p_date,3,IFSQ_UPDATE_SEQNO.NEXTVAL);
insert into sales values(p_itemid,p_date,2,IFSQ_UPDATE_SEQNO.NEXTVAL);
with sales1 as (select row_num1,a.* from (select rownum row_num1,b.*
from sales b order by update_seq_no asc)a)
select a.*
from sales1 a,sales1 b
where a.row_num1-1=b.row_num1
and a.code=2 and b.code=3
and a.itemid=b.itemid
and a.Date=b.Date;
If there is only one row per itemid and code, then you can simply aggregate:
select itemid
from mytable
group by itemid
having max(case when code = 2 then date end) > max(case when code = 3 then date end);
With your sample data, however, no itemid will be returned, as all codes per itemid have the same date. None comes before the other.

Iteration SQL Query?

I have very difficult scenario in my Oracle DB. I have a table setup as following:
ID File_route_group CollectID DeliveryID Sequence
---------- ------------ --------- ------------ --------
11483 12 123 411 1
12174 12 411 523 2
12465 12 523 611 3
12165 46 215 662 1
52462 12 266 394 4
12199 46 662 718 2
61244 12 394 980 5
Each line represents a "route" a certain file has taken and the following row (next sequence) should always have the same CollectID as the previous sequences DeliveryID.
In this example row ID 52462 is wrong. It should have collectID 611 (as row with sequence 3 have that as its deliveryID).
Is there a way to create a query that would have returned this row (with id 52462)? Like following:
ID File_route_group CollectID DeliveryID Sequence
---------- ------------ --------- ------------ --------
52462 12 266 394 4
WITH cte AS
( SELECT ID,
File_route_group,
CollectID,
DeliveryID,
Sequence,
LAG(DeliveryID, 1) OVER (PARTITION BY File_route_group
ORDER BY Sequence)
Previous_DeliveryID
FROM Routes
)
SELECT *
FROM cte
WHERE ( CollectID <> Previous_DeliveryID
OR CollectID IS NULL
OR Previous_DeliveryID IS NULL
)
AND Sequence > 1 ;
Tested at SQL-Fiddle
You can join the table with itself to represent the relation between the 'current' and the 'previous' entry:
SELECT * FROM route current_route
JOIN route last_route
ON current_route.Sequence = last_route.Sequence + 1
AND current_route.File_route_grp = last_route.File_route_grp
WHERE current_route.CollectID <> last_route.DeliveryID
Note that while the usage of LAG() proposed in the other answers is the more elegant and possibly more performant way of doing this in Oracle, this solution is more portable and also works, e.g. with MySQL and other DBMS.
In a derived table use the LAG function to get the PreviousDeliveryID then filter that where the CollectionId does not equal PreviousDeliveryID
SELECT ID, Customer_id, CollectID, DeliveryID, Sequence
FROM (
SELECT ID, Customer_id, CollectID, DeliveryID, Sequence, LAG(DeliveryID, 1) OVER(ORDER BY ID) AS PrevDeliveryID
FROM Routes
) AS t
WHERE t.CollectionID <> t.PrevDeliveryID