I have table like this:
Then I want to change the table to this:
I usually doing this in Power BI Query Editor but the file is too big so I try in another platform like Bigquery/Postgresql
Thank you
Consider below solution (for BigQuery)
select Fruits, regexp_extract(Store, r'Qty_(.*?)_Value') Store, Qty, Value
from your_table
unpivot (
(Qty, Value) for
Store in (
(Qty_Store_A, Value_Store_A),
(Qty_Store_B_C, Value_Store_B_C),
(Qty_Store_D_C, Value_Store_D_C)
)
)
If applied to sample data in your question - output is
And also, slightly modified version of above where you can define Store Names explicitly if for some reason RegEx way does not work for you
select Fruits, Store, Qty, Value
from your_table
unpivot (
(Qty, Value) for
Store in (
(Qty_Store_A, Value_Store_A) as 'Store_A',
(Qty_Store_B_C, Value_Store_B_C) as 'Store_B_C',
(Qty_Store_D_C, Value_Store_D_C) as 'Store_D_C'
)
)
Obviously, with same output
You can cross join to a a values clause:
select t.fruits,
u.*
from the_table t
cross join lateral (
values ('Store A', t.qty_store_a, t.value_store_a),
('Store B', t.qty_store_b, t.value_store_b),
('Store C', t.qty_store_c, t.value_store_c),
('Store BC', t.qty_store_bc, t.value_store_bc),
('Store DC', t.qty_store_dc, t.value_store_dc)
) as u(store, qty, value)
Related
I have a scenario wherein I have to remove all the strings except a or b or c
My sample table is as follows:
Id Product
------------------
1. a,b,Da,c
2. Ty,a,b,c
3. a,sds,b
Sample output
Id Product
----------------
1. a,b,c
2. a,b,c
3. a,b
My current version is Microsoft SQL Server 2008 R2
This should help you out. As I state in the comments, I make use of Jeff Moden's DelimitedSplit8k, as you're using an older version of SQL Server. if you were using 2016+, you would have access to STRING_SPLIT. I also normalise your data; as storing delimited data is almost always a bad idea.
CREATE TABLE #Sample (id int, Product varchar(20));
INSERT INTO #Sample
VALUES (1,'a,b,Da,c'),
(2,'Ty,a,b,c'),
(3,'a,sds,b');
GO
--The first problem you have is you're storing delimited data
--You really should be storing each item on a separate row.
--This is, however, quite easy to do. i'm going to use a different
--table, however, you can change this fairly easily for your
--needs.
CREATE TABLE #Sample2 (id int, Product varchar(2));
GO
--You can split the data out by using a Splitter.
--My personal preference is Jeff Moden's DelimitedSplit8K
--which I've linked to above.
INSERT INTO #Sample2 (id, Product)
SELECT id, Item AS Product
FROM #Sample S
CROSS APPLY dbo.DelimitedSplit8K(S.Product,',') DS
WHERE DS.Item IN ('a','b','c');
GO
--And hey presto! Your normalised data, and without the unwanted values
SELECT *
FROM #Sample2;
GO
DROP TABLE #Sample;
DROP TABLE #Sample2;
If you have to keep the delimited format, you can use STUFF and FOR XML PATH:
WITH Split AS(
SELECT id,
Item AS Product,
ItemNumber
FROM #Sample S
CROSS APPLY dbo.DelimitedSplit8K(S.Product,',') DS
WHERE DS.Item IN ('a','b','c'))
SELECT id,
STUFF((SELECT ',' + Product
FROM Split sq
WHERE sq.id = S.id
ORDER BY ItemNumber
FOR XML PATH('')),1,1,'')
FROM Split S
GROUP BY id;
This also will do the thing, using xml only:
select * into #t from (values('a,b,Da,c'),('Ty,a,b,c'),('a,sds,b'))v(Product)
;
with x as (
SELECT t.Product, st.sProduct
FROM #t t
cross apply (
SELECT CAST(N'<root><r>' + REPLACE(t.Product,',', N'</r><r>') + N'</r></root>' as xml) xProduct
)xt
cross apply (
select CAST(r.value('.','NVARCHAR(MAX)') as nvarchar) sProduct
from xt.xProduct.nodes(N'//root/r') AS RECORDS(r)
) st
where st.sProduct in ('a', 'b', 'c')
)
select distinct x.Product, REVERSE(SUBSTRING(REVERSE(cleared.cProduct), 2, 999)) cleared
from x
cross apply ( select (
select distinct ref.sProduct + ','
from x ref
where ref.Product = x.Product
for xml path('') )
)cleared(cProduct)
;
drop table #t
I currently have the following query:
WITH History AS (
SELECT
kz.*,
kz.__$operation AS operation,
map.tran_begin_time as beginT,
map.tran_end_time as endT
FROM cdc.fn_cdc_get_all_changes_dbo_EXT_GeolObject_KategZalezh(sys.fn_cdc_get_min_lsn('dbo_EXT_GeolObject_KategZalezh'), sys.fn_cdc_get_max_lsn(), 'all') AS kz
INNER JOIN [cdc].[lsn_time_mapping] map
ON kz.[__$start_lsn] = map.start_lsn
where kz.GUID_BalanceHC_Zalezh = 'DDA9AB3A-A0AF-4623-9362-0000C8C83D63'
),
UnpivotedValues AS(
SELECT guid, GUID_another, field, val, operation, beginT, endT
FROM History
UNPIVOT ( [val] FOR field IN
(
area,
oilwidthmin,
oilwidthmax,
efectivwidthmin,
efectivwidthmax,
etc...
))t
),
UnpivotedWithLastValue AS (
SELECT
*,
--Use LAG() to get the last value for the same field
LAG(val, 1) OVER (PARTITION BY guid, GUID_another, field ORDER BY BeginT) LastVal
FROM UnpivotedValues
)
SELECT * FROM UnpivotedWithLastValue WHERE val <> LastVal OR LastVal IS NULL ORDER BY guid
This query returns the changed values for a single table that has CDC (Change Data Capture) enabled.
I want to create a stored procedure that receives the columns to be unpivoted, and the cdc function (e.g. cdc.fn_cdc_get_all_...) as parameters and returns the result set.
The result for this tables must be joined in one report.
In my case parameter 1 is cdc.fn_cdc_get_all_changes_dbo_EXT_GeolObject_KategZalezh(sys.fn_cdc_get_min_lsn('dbo_EXT_GeolObject_KategZalezh'), sys.fn_cdc_get_max_lsn(), 'all'). This is the CDC function.
How should I send the list of fields that i want in the result? How's the string?
Also, is there a way to do without dynamic SQL? Dynamic SQL it is not better solution for performance.
As you know SQL Server is declarative by design and does not support macro substitution.
UNPIVOT would clearly be more performant, but here is a simplified example of a UNPIVOT which does not require Dynamic SQL, but only a little XML.
Example
Let's assume your table/results looks like this:
You may notice that I only we only specify key fields to EXCLUDE in the final WHERE
Declare #YourData table (ID int,Active bit,First_Name varchar(50),Last_Name varchar(50),EMail varchar(50),Salary decimal(10,2))
Insert into #YourData values
(1,1,'John','Smith','john.smith#email.com',85600),
(2,0,'Jane','Doe' ,'jane.doe#email.com',83200)
;with cte as (
-- Replace with your Complex Query
Select * from #YourData
)
Select A.ID
,A.Active
,C.*
From cte A
Cross Apply (Select XMLData=cast((Select A.* for XML RAW) as xml)) B
Cross Apply (
Select Item = attr.value('local-name(.)','varchar(100)')
,Value = attr.value('.','varchar(max)')
From XMLData.nodes('/row') C1(n)
Cross Apply C1.n.nodes('./#*') C2(attr)
Where attr.value('local-name(.)','varchar(100)') not in ('ID','Active')
) C
Returns
I would like to produce a string containing some parsed numeric ranges.
I have a table with some data
b_id,s_id
1,50
1,51
1,53
1,61
1,62
1,63
2,91
2,95
2,96
2,97
Using only SQL in PostgreSQL, how could I produce this output:
b_id,s_seqs
1,"50-51,53,61-63"
2,"91,95-97"
How on earth do I do that?
select b_id, string_agg(seq, ',' order by seq_no) as s_seqs
from (
select
b_id, seq_no,
replace(regexp_replace(string_agg(s_id::text, ','), ',.+,', '-'), ',', '-') seq
from (
select
b_id, s_id,
sum(mark) over w as seq_no
from (
select
b_id, s_id,
(s_id- 1 <> lag(s_id, 1, s_id) over w)::int as mark
from my_table
window w as (partition by b_id order by s_id)
) s
window w as (partition by b_id order by s_id)
) s
group by 1, 2
) s
group by 1;
Here you can find a step-by-step analyse from the innermost query towards the outside.
(Please note, I require a SQL Server 2005 solution)
I have a UNION query, where the first part returns multiple rows in a particular order, and the second part returns a single row which MUST the last row of the result set.
The easiest way I've found so far is to include an extra "sort" column, BUT I do not want this column to be returned with the data set.
Please note, this example has a single column, but the real query has many columns, built via dynamic query...
SELECT [TITLE],
(SELECT COUNT(*) FROM dbo.[OTHERTABLE] WHERE ...) AS [VALUE],
0 AS [EXTRAORDER]
FROM dbo.[LOOKUPTABLE]
UNION
SELECT 'Total',
(SELECT COUNT(*) FROM dbo.[OTHERTABLE]),
1 AS [EXTRAORDER]
ORDER BY [EXTRAORDER], [TITLE]
How can I creating this so that all the columns excluding EXTRAORDER are returned (preferably without manually listing all the desired columns)?
Unless anybody can come up with a better solution, I have currently settled for the following...
(I was heading down the same route as SQLhint.com was in their answer. Unfortunately their answer - at the time of writing - is still incorrect, and therefore I cannot upvote it. The Total row will still be ordered within the results of the main SELECT, rather than be "appended" to the end.)
Ideally I wanted a solution that didn't require the replication of all the columns required in the final data set. Unfortunately this solution does NOT satisfy this requirement, but at least it works!
The solution was to use CTE...
; WITH [DATA] AS (
SELECT [TITLE],
(SELECT COUNT(*) FROM dbo.[OTHERTABLE] WHERE ...) AS [VALUE],
0 AS [EXTRAORDER]
FROM dbo.[LOOKUPTABLE]
UNION
SELECT 'Total',
(SELECT COUNT(*) FROM dbo.[OTHERTABLE]),
1
)
SELECT [TITLE], [VALUE]
FROM [DATA]
ORDER BY [EXTRAORDER], [TITLE]
I think the best way is to return 2 result sets, but to respond strictly to your question:
SELECT [title], [value]
FROM
(SELECT [TITLE],
(SELECT COUNT(*) FROM dbo.[OTHERTABLE] WHERE ...) AS [VALUE],
0 AS [EXTRAORDER]
FROM dbo.[LOOKUPTABLE]
UNION
SELECT 'Total',
(SELECT COUNT(*) FROM dbo.[OTHERTABLE]),
1 AS [EXTRAORDER]) as A
ORDER BY CASE WHEN [title] = 'Total' THEN 'zzz' ELSE [title] END
How about this?
SELECT [TITLE], [VALUE]
FROM (
SELECT [TITLE],
(SELECT COUNT(*) FROM dbo.[OTHERTABLE] WHERE ...) AS [VALUE]
FROM dbo.[LOOKUPTABLE]
UNION
SELECT 'Total',
(SELECT COUNT(*) FROM dbo.[OTHERTABLE])
) [DATA]
ORDER BY (case when [TITLE] = 'Total' then 1 else 0 end), [TITLE]
This removed the [EXTRAORDER] column but still orders based on the [TITLE] treating 'Total' as the last item.
I have a data in the field as " Date: 03-21-13 12/13/14/15 Date:04-21-13 39/12/34/14 Date:04-19-13 19/45/65/12 ".How to sort this data inside the field based on the recent Date.
It should Look like
Date:04-21-13 39/12/34/14
Date:04-19-13 19/45/65/12
Date: 03-21-13 12/13/14/15
Because you are storing it as text, you cannot correctly sort directly on the column (as you appear to have discovered). You will need to split the column, and then sort on that. Something like:
Declare #tvTable Table (
TextColumn varchar(max)
)
Insert #tvTable
Select '04-19-13 19/45/65/12'
Union All
Select '04-21-13 39/12/34/14'
Union All
Select '03-21-13 12/13/14/15'
Union All
Select '03-25-13 17/18/19/20'
Union All
Select '05-01-13 99/88/77/66'
Union All
Select '02-01-13 11/22/33/44'
Select t.TextColumn
From #tvTable t
Cross Apply dbo.fncDelimitedSplit8k(TextColumn, ' ') split
Where split.ItemNumber = 1
Order By Cast(split.Item As DateTime) Desc
The split function taken from Jeff Moden Tally OH!