How to convert dynamic column into rows in snowflake - snowflake-cloud-data-platform

INPUT
product
country
brand
01-01-2022
02-01-2022
03-01-2022
dairy milk
India
Cadbury
10
20
30
OUTPUT
product
country
brand
DATE
VALUE
dairy milk
India
Cadbury
01-01-2022
10
dairy milk
India
Cadbury
02-01-2022
20
dairy milk
India
Cadbury
03-01-2022
30
INPUT
product
country
brand
01-01-2022
02-01-2022
03-01-2022
04-01-2022
dairy milk
India
Cadbury
10
20
30
40
OUTPUT
product
country
brand
DATE
VALUE
dairy milk
India
Cadbury
01-01-2022
10
dairy milk
India
Cadbury
02-01-2022
20
dairy milk
India
Cadbury
03-01-2022
30
dairy milk
India
Cadbury
04-01-2022
40

Here's a dynamic solution using object_construct and lateral flatten .
First create some test data.
create or replace view data as
SELECT
*
FROM
(VALUES (
'dairy milk',
'India',
'Cadbury',
10,
20,
30))
as v (PRODUCT,
COUNTRY,
BRAND,
"01-01-2022",
"02-01-2022",
"03-01-2022")
;
I assume your date columns are quoted, although not shown as such in your question, as otherwise they are invalid column names.
with
-- First create an object containing the contents of each row
ro as (select
PRODUCT, COUNTRY, BRAND,
object_construct(*) row_obj
from data)
-- Lateral flatten the object, and filter out the columns that you don't want to pivot.
Select
PRODUCT, COUNTRY, BRAND,
to_date( -- Note: Removing the " from quoted column names
replace(key,'"')
,'DD-MM-YYYY') as "DATE", value
from ro, lateral flatten (input => row_obj)
where key not in ('PRODUCT','COUNTRY','BRAND');
I've assumed that you want the DATE column in the result to be returned as a date type, hence the need for replace and cast to convert the column names you are un-pivoting. If you are fine with the DATE column as varchar type, you can replace
to_date(
replace(key,'"')
,'DD-MM-YYYY') as "DATE"
with
key as "DATE"
Note: your columname DATE is a keyword and therefore needs to be quoted. I think its good practice to avoid using SQL keywords as object-names.

One approach uses a UNION ALL:
SELECT product, country, brand, '2022-01-01' AS DATE, "01-01-2022" AS VALUE FROM yourTable
UNION ALL
SELECT product, country, brand, '2022-01-02', "02-01-2022" FROM yourTable
UNION ALL
SELECT product, country, brand, '2022-01-03', "03-01-2022" FROM yourTable;

Related

How to collect all deference in rows between two periods?

I'm trying to see the difference between the two periods for a column.
For example, we see that sales decreased at the end of the month, and we need to see which products were not sold at the end of the month?
I can create SELECT to see quantity for each product for each period:
SELECT product_id, count(product_id) AS Count
FROM testDB
WHERE
sales_date IS NOT NULL
AND
delivery_date BETWEEN '2021-02-01 00:00:03.0000000' AND '2021-02-14 23:56:00.0000000'
GROUP BY
product_id
and the same SELECT with another period:
delivery_date BETWEEN '2021-02-14 00:00:03.0000000' AND '2021-02-28 23:56:00.0000000'
So, after these queries I see list for first period with 10 products with quantity and in second period I see list with 7 products with quantity. I can't get the difference between the lists of the two SELECTs. I tried to use != and NOT IN but without any results.
I will be very grateful for your help. Thanks
Sorry for the confusion. I meant the difference between the two selects:
The result of the first one (for first period):
Product_ID Count
grapes. 100
lime. 13
lemon. 15
cherry. 222
blueberry. 123
banana. 1
apple. 123
watermelon 56
and second one (for second period):
Product_ID Count
grapes. 10
lime. 1
lemon. 10
cherry. 2
blueberry. 13
banana. 12
and I wand to see difference between these selects:
Product_ID Count
apple. 0
watermelon. 0
So we did not sell any apples and watermelons in second period.
SELECT product_id, count(product_id) AS Count,delivery_date-sales_date as DIFFERENCE
FROM testDB
WHERE
sales_date IS NOT NULL
AND
delivery_date BETWEEN '2021-02-01 00:00:03.0000000' AND '2021-02-14 23:56:00.0000000'
GROUP BY
product_id
This should work for getting the difference between the 2 period columns.

Outputting a column as a different number from a different table

The goal is to rank the Movies table according to quantity in the Inventory table such that for each duplicate value, it skips the subsequent value so that the next non-duplicate value remains in its rightful position. Display MovieID, Latest Title, Price, and the Rank.
WhileMovieId ‘1’ from Movies table corresponds to MovieId ‘101’ of your Movie inventory table and so on.
These are the tables
Movies
MovieId
latest title
Price
1
Breaking Dawn
200.00
2
The Proposal
185.00
3
Iron Man 2
180.00
4
Up
180.00
5
The Karate Kid
190.00
6
How to train your Dragon
190.00
7
Spiderman 3
195.00
Movie Inventory
MovieId
Quantity
101
3
105
4
107
5
108
7
110
8
111
4
And this is my attempt at the code that is showing a lot of NULL
SELECT CASE
WHEN Movies.MovieId + 100 = MovieInventory.MovieID
THEN CAST(MovieInventory.MovieID AS INT)
END AS 'MovieId',
Movies.LatestTitle, Movies.Price,
DENSE_RANK() OVER (ORDER BY Movies.MovieId DESC) AS [Rank]
FROM Movies, MovieInventory WHERE MovieInventory.MovieID IS NOT NULL
GO
This is what you need.
Notes:
You need RANK not DENSE_RANK to achieve the result you want
You need to order by Quantity
Use proper JOIN syntax, not comma , joins
Use table aliases for better readability
The foreign and primary key relationships are weird: mi.MovieID appears to be varchar but when converted to int is 100 more than m.MovieID???
The calculation in the SELECT is not accessible to the JOIN conditions
Don't use apostrophes '' to quote column names
SELECT
mi.MovieId,
m.LatestTitle,
m.Price,
RANK() OVER (ORDER BY mi.Quantity DESC) AS [Rank]
FROM Movies m
JOIN MovieInventory mi ON TRY_CAST(mi.MovieID AS int) = m.MovieID + 100;

To Display a Stock Details

I am trying to display the stock details like MRP, Sales Rate , Tax Amount and so on.
I used three grid views to make a entry in my form. They are,
GV Purchase
GV Product Details - (It is used to show the product name and product code )
GV Stock Details - (It is used to show the Quantity, MRP, Sales Price and so on)
I used SQL Query for populate the DB records in my grid view.
I select a Product A from Product Grid view in my form ,the Stock grid will show the corresponding data of the Product A
My DB records,
code Name QTY MRP S.Rate
aa11 Pro A 5 120.00 130.00
aa11 Pro A 2 130.00 150.00
aa12 Pro B 4 100.00 110.00
aa13 Pro C 2 50.00 60.00
When I select Pro A in GV Product Details in my form. The GV Stock Details will be shown the QTY, MRP, S.Rate for the Pro A
But My Query returns like this format
aa11 Pro A 5 120.00 130.00
2 130.00 150.00
4 100.00 110.00
2 50.00 60.00
This is my Query,
select s.*,iif( d.NewSalePrice is null,s.saleprice1,d.NewSalePrice)as NewSalePrice,Pdate from (select s.*,p.ProductFullName,p.ProductCode from Stock s,Product p where s.productfullcode=p.ProductFullCode) s left join(select Productfullcode,MRP,PUnitPrice,NewSalePrice,Pdate from DailyPricing ) d on s.ProductFullCode=d.ProductFullCode where s.MRP=d.MRP and s.UnitPrice=d.PUnitPrice and Pdate=(select MAX(Pdate) from DailyPricing where PUnitPrice=s.UnitPrice and mrp=s.MRP and ProductFullCode=s.ProductFullCode) order by ProductFullCode
How to I get Stock details only matched by Pro A
Note : These table format is just a model for my Table
Thanks in Advance,
Vinayak vk.:-)
You could probably simplify your query, and add a where clause for the product name you need.
Haven't tested it against mock data, but something like this SQL:
select
s.*,
p.ProductFullName,
p.ProductCode,
coalesce(d.NewSalePrice, s.saleprice1) as SalePrice,
max(d.Pdate) over (partition by p.ProductFullCode, s.mrp, s.UnitPrice) as MaxPdate
from Product p
join Stock s on s.productfullcode = p.ProductFullCode
join DailyPricing d on (s.ProductFullCode = d.ProductFullCode and s.MRP = d.MRP and s.UnitPrice = d.PUnitPrice)
where p.ProductFullName = 'Pro A'
order by p.ProductFullCode

SQL query to calculate Throughput based "subtracting" two Select statements using Group By

I'm trying to formulate a SQL query to calculate the difference in the number of people "arriving" and "departing" grouped by City and Date.
TravelerID ArrivalDate DepartureDate City
1 2015-10-01 2015-10-03 New York
2 2015-10-02 2015-10-03 New York
3 2015-10-02 2015-10-04 Chicago
4 2015-10-01 2015-10-02 Chicago
I'm hoping to get a table that looks like
NumOfTravelers Date City
1 2015-10-01 New York
1 2015-10-02 New York
-2 2015-10-03 New York
1 2015-10-01 Chicago
0 2015-10-02 Chicago
-1 2015-10-04 Chicago
A positive number for NumOfTravelers means that more people arrived in that city on that particular date. A negative number for NumOfTravelers means that more people left that city on that particular date.
In trying to break down this SQL query, I've tried
SELECT COUNT(TravelerID) as NumTravelersArrivng, ArrivalDate, City FROM TravelTable GROUP BY ArrivalDate, City;
SELECT COUNT(TravelerID) as NumTravelersDeparting, DepartureDate, City FROM TravelTable GROUP BY DepartureDate, City;
I'm trying to get "NumTravelersArriving" - "NumTravelersDeparting" into a column that represents "traveler throughput" grouped by City and Date.
I've been so stumped on this. I'm using SQL Server, and having a frustrating time using Table aliases and Column aliases.
Try this:
SELECT *
FROM (
SELECT City, ArrivalDate As Date, COUNT(TravelerID) As NumOfTravelers
FROM TravelTable
GROUP BY City, ArrivalDate
) a
FULL JOIN (
SELECT City, DepartureDate As Date, COUNT(TravelerID) * -1 As NumOfTravelers
FROM TravelTable
GROUP BY City, DepartureDate
) b ON b.City = a.City AND b.Date = a.Date

Use Sum in certain conditions

I have say the following rows
Country Population
IE 30
IE 20
UK 15
DE 20
DE 10
UK 20
BE 5
So basically I want to net the values together only for IE and DE... the rest I just want the values
So this would sum them all ..
Select Country, Sum(Population) From CountryPopulation group by Country
and I can add a where clause to exclude all other countries except IE and DE... but I also want these in the result set but just not summed.
So the table above would look like this when summed
Country Population
IE 50 -- Summed
UK 15 -- Orginal Value
DE 30 -- Summed
UK 20 -- Orginal Value
BE 5 -- Orginal Value
Problem is I can’t get a sum if, or case to work as the query has to be aggregated by group by. Only other way I can thing on is to
Sum all the IE and DE and union it with the rest of the data..
Or
Maybe use a CTE
Is there a nice slick way of doing this....
Select Country, Sum(Population)
From CountryPopulation
group by case when Country in ('IE','DE')
then 'IE_DE'
else Country
end
declare #t table (Country char(2), Population int)
insert into #t (Country, Population) values
('IE',30),
('IE',20),
('UK',15),
('DE',20),
('DE',10),
('UK',20),
('BE',5 )
; With Ordered as (
select Country,Population,CASE
WHEN Country in ('IE','DE') THEN 1
ELSE ROW_NUMBER() OVER (ORDER BY Country)
END as rn
from #t
)
select Country,rn,SUM(Population)
from Ordered
group by Country,rn
Produces:
Country rn
------- -------------------- -----------
BE 1 5
DE 1 30
IE 1 50
UK 6 15
UK 7 20
The trick is to just introduce a unique value for each row, except for the IE and DE rows that all get a 1. If the source rows all, actually, already have such a unique value then the CTE can be simplified (or avoided, at the expense of having to place the CASE expression in the GROUP BY as well as the SELECT)
You could also use UNION ALL and divide this query into two:
SELECT P.country,
P.population
FROM (SELECT country,
Population = Sum(population)
FROM dbo.countrypopulation cp
WHERE country IN ( 'IE', 'DE' )
GROUP BY country
UNION ALL
SELECT country, population
FROM dbo.countrypopulation cp
WHERE country NOT IN ( 'IE', 'DE' )
) P
ORDER BY P.population DESC
Even if this is not so concise it is readable and efficient.
sql-fiddle

Resources