I have the following table and I need to flatten the data.
From here
table 1
id val1 val2 ....valn
-----------------------------------
1 a z
1 b x
1 c v
2 a w
2 b q
..n
To here
id val1_1 val2_1 ....valn_1 val1_2 val2_2 ... valn_2 .... val1_n...valn_n
-----------------------------------
1 a z b x
2 a w b q
..n
Any ideas?
I have done something using cursors, however it is a little nasty and It has some bugs. Pivot?
Yes you need to a pivot, but you also need to calculate a row-number to pivot against. However, given this is a multi-column pivot, it's probably easier to just use conditional aggregation.
I suggest you pick a proper ordering for the row-number
SELECT
id,
val1_1 = MAX(CASE WHEN t.rn = 1 THEN t.val1 END),
val2_1 = MAX(CASE WHEN t.rn = 1 THEN t.val2 END),
val3_1 = MAX(CASE WHEN t.rn = 1 THEN t.val3 END),
-- ...........
val1_2 = MAX(CASE WHEN t.rn = 2 THEN t.val1 END),
val2_2 = MAX(CASE WHEN t.rn = 2 THEN t.val2 END),
val3_2 = MAX(CASE WHEN t.rn = 2 THEN t.val3 END),
-- ...........
FROM (
SELECT *,
rn = ROW_NUMBER() OVER (PARTITION BY t.rn ORDER BY (SELECT 1))
FROM table1 t
) t
GROUP BY t.id;
CodeDt CodeHeader Item Qty Type Remark Attachment
LK4-033502 RK-K-LK4-032438 IA01001023 2.00 TPR002 2 1.jpeg
LK4-033502RK RK-K-LK4-032438 IA01001023RK 2.00 NULL IA01001023 NULL
Above is my data from Sql server table (using 2008 R2). I want to make it one row only. Here is my expected result:
CodeDt CodeHeader Item NewItem Qty
LK4-033502 RK-K-LK4-032438 IA01001023 IA01001023RK 2.00
How can I achieve that? Here is the relation:
Row 1 Item = Row 2 Remark,
Row 1 Code DT = Row 2 CodeDt+'RK'
There are several solutions
1) Using JOIN. It assumes that Type field is null for rows with CodeDt+'RK'
select
a.CodeDt, a.CodeHeader, a.Item, b.Item, a.Qty
from
myTable a
join myTable b on a.Item = b.Remark
where
a.Type is not null
2) Conditional aggregation
select
max(case when rn = 1 then CodeDt end)
, CodeHeader
, max(case when rn = 1 then Item end)
, max(case when rn = 2 then Item end)
, max(case when rn = 1 then Qty end)
from (
select
*, rn = row_number() over (partition by CodeHeader order by CodeDt)
from
myTable
) t
group by CodeHeader
I have this query
SELECT [b].[BillID],
[b].[BillingCode],
[bc].[CurrentUsage],
[bc].[Rate],
[bc].[CurrentCost],
[c].[Title]
INTO #Temp
FROM [dbo].[Bills] AS [b]
INNER JOIN [dbo].[BillCosts] AS [bc] ON [bc].[BillIDRef] = [b].[BillID]
INNER JOIN [Base].[Costs] AS [c] ON [c].[CostID] = [bc].[CostIDRef]
this is result
BillID BillingCode CurrentUsage Rate CurrentCost Title
----------- -------------------- ------------ ----------- ----------- ------
5 44545455 10 20 30 AvgTimes
5 44545455 40 50 60 MaxTimes
I need this result:
BillID BillingCode AvgTimes Cost MaxTimes Cost AvgTimes Rate MaxTimes Rate AvgTimes Usage MaxTimes Usage
----------- -------------------- -------------- -------------- -------------- ------------- -------------- ---------------
5 44545455 30 60 20 50 10 40
Is it possible using pivot on multiple columns CurrentUsage,Cost,Rate?
If isn't possible using pivot, How to write query?
You can use conditional aggregation:
SELECT [b].[BillID],
[b].[BillingCode],
MAX(CASE WHEN [c].[Title] = 'AvgTimes' THEN [bc].[CurrentUsage] END) AS [AvgTimes Usage],
MAX(CASE WHEN [c].[Title] = 'MaxTimes' THEN [bc].[CurrentUsage] END) AS [MaxTimes Rate],
MAX(CASE WHEN [bc].[Title] = 'AvgTimes' THEN [bc].[Rate] END) AS [AvgTimes Rate],
MAX(CASE WHEN [bc].[Title] = 'MaxTimes' THEN [bc].[Rate] END) AS [MaxTimes Usage],
MAX(CASE WHEN [bc].[Title] = 'AvgTimes' THEN [bc].[CurrentCost] END) AS [AvgTimes CurrentCost],
MAX(CASE WHEN [bc].[Title] = 'MaxTimes' THEN [bc].[CurrentCost] END) AS [MaxTimes CurrentCost]
INTO #Temp
FROM [dbo].[Bills] AS [b]
INNER JOIN [dbo].[BillCosts] AS [bc] ON [bc].[BillIDRef] = [b].[BillID]
INNER JOIN [Base].[Costs] AS [c] ON [c].[CostID] = [bc].[CostIDRef]
GROUP BY [b].[BillID], [b].[BillingCode]
I have the following data on a MSSQL Server:
Item No_ Unit Of Measure Qty Per Base UoM Rounding Precision
000001 PIECE 1 1.0
000001 PALLET 100 1.0
000001 BOX 12 1.0
000002 KG 1 1.0
000002 TON 1000 0.001
I am trying to get the following output:
Item No_ UoM1 Qty pb UoM1 RP1 UoM2 Qty pb UoM2 RP2 UoM3 Qty pb UoM3 RP3
000001 PIECE 1 1.0 PALLET 100 1.0 BOX 12 1.0
000002 KG 1 1.0 TON 1000 0.0001 NULL NULL NULL
I tried achieving this using the PIVOT operator but it does not seem te be correct.
What is the correct way to achieve my desired output?
For trully generic solution you need to use Dynamic-SQL.
But you say you want starting point, so I give you one. You can use:
LiveDemo
WITH cte AS
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY Item_No_ ORDER BY (SELECT 1)) AS rn
FROM #mytable
)
select Item_No_,
max(case when rn = 1 then Unit_Of_Measure end) UoM1,
max(case when rn = 1 then Qty_Per_Base_UoM end) [Qty pb UoM1],
max(case when rn = 1 then Rounding_Precision end) RP1,
max(case when rn = 2 then Unit_Of_Measure end) UoM2,
max(case when rn = 2 then Qty_Per_Base_UoM end) [Qty pb UoM2],
max(case when rn = 2 then Rounding_Precision end) RP2,
max(case when rn = 3 then Unit_Of_Measure end) UoM3,
max(case when rn = 3 then Qty_Per_Base_UoM end) [Qty pb UoM3],
max(case when rn = 3 then Rounding_Precision end) RP3,
max(case when rn = 4 then Unit_Of_Measure end) UoM4,
max(case when rn = 4 then Qty_Per_Base_UoM end) [Qty pb UoM4],
max(case when rn = 4 then Rounding_Precision end) RP4
from cte
group by Item_No_;
The point is that if your number of units is known in advance you can create hardcoded columns from 1 .. n.
For more info search Dynamic Pivot multiple columns. It is achievable, but first try to crack this solution.
I have this table:
lnumber | lname | bez_gem
---------+----------------+------------------------------
1 | name1 | Berg b.Neumarkt i.d.OPf.
1 | name1 | Altdorf b.Nürnberg
2 | name2 | Berg b.Neumarkt i.d.OPf.
2 | name2 | Altdorf b.Nürnberg
3 | name3 | Mainleus
3 | name3 | Weismain
4 | name4 | Weismain
4 | name4 | Mainleus
The code for the query is:
WITH double AS (
SELECT
partnumber,
bez_gem
FROM accumulation a, municipality b
WHERE ST_Intersects(a.geom, b.geom)
AND EXISTS (
SELECT
lnumber
FROM mun_more_than_once c
WHERE a.partnumber=c.lnumber)
ORDER BY partnumber)
SELECT
landslide.lnumber,
lname,
bez_gem
FROM double, landslide
WHERE double.partnumber=landslide.lnumber
ORDER BY lnumber
I want to transpose in this format
lnumber | lname | bez_gem1 | bez_gem2
---------+----------------+------------------------------------------------
1 | name1 | Berg b.Neumarkt i.d.OPf. | Altdorf b.Nürnberg
2 | name2 | Berg b.Neumarkt i.d.OPf. | Altdorf b.Nürnberg
It depends. If you always have two bez_gem per lnumber, you can simply use:
SELECT lnumber, lname
, min(bez_gem) AS bez_gem1
, max(bez_gem) AS bez_gem2
FROM test
GROUP BY 1,2
ORDER BY 1;
SQL Fiddle.
Note that the order of peers is undefined in your question. Collation rules (alphabetical order) decide in my example.
For an actual cross tabulation you would use the crosstab() function from the additional module tablefunc. But your table is missing a category name (no indication which row holds bez_gem1 and which bez_gem2). Explanation, details and links:
PostgreSQL Crosstab Query
SQLFiddle
Data
-- drop table if exists test;
create table test (lnumber int, lname varchar, bez_gem varchar);
insert into test values
(1 , 'name1' , 'Berg b.Neumarkt i.d.OPf.'),
(1 , 'name1' , 'Altdorf b.Nürnberg'),
(2 , 'name2' , 'Berg b.Neumarkt i.d.OPf.'),
(2 , 'name2' , 'Altdorf b.Nürnberg'),
(3 , 'name3' , 'Mainleus'),
(3 , 'name3' , 'Weismain'),
(4 , 'name4' , 'Weismain'),
(4 , 'name4' , 'Mainleus'),
(4 , 'name4' , 'XXMainleus')
;
Query
select
lnumber,
lname,
max(case when rn = 1 then bez_gem end) as bez_gem1,
max(case when rn = 2 then bez_gem end) as bez_gem2,
max(case when rn = 3 then bez_gem end) as bez_gem3
from
(
select
*,
row_number() over(partition by lname) rn
from
test
) a
group by
lnumber,
lname
Result
1;name1;Berg b.Neumarkt i.d.OPf.;Altdorf b.Nürnberg;
2;name2;Berg b.Neumarkt i.d.OPf.;Altdorf b.Nürnberg;
3;name3;Mainleus;Weismain;
4;name4;Weismain;Mainleus;XXMainleus
Old Answer
If you have only two possible rows for every lnumber (you should add this important info to your question), you can simply use min and max:
WITH double AS (
SELECT
partnumber,
bez_gem
FROM accumulation a, municipality b
WHERE ST_Intersects(a.geom, b.geom)
AND EXISTS (
SELECT
lnumber
FROM mun_more_than_once c
WHERE a.partnumber=c.lnumber)
ORDER BY partnumber)
SELECT
landslide.lnumber,
lname,
min(bez_gem) as bez_gem1,
max(bez_gem) as bez_gem2
FROM double, landslide
WHERE double.partnumber=landslide.lnumber
group by
landslide.lnumber,
lname
ORDER BY lnumber
If you have possibly more than two rows for every lnumber and you really need crosstab, there is a lot of questions regarding crosstab in PostgreSQL on SO (example). As an alternative you can try the following approach.
Because this is one-time analysis, you can easily get maximum number of unique bez_gem values:
select
landslide.lnumber,
count(distinct bez_gem) cnt
from
<<some_data>>
group by
landslide.lnumber
order by
cnt desc limit 1
Then you can use:
select
landslide.lnumber,
lname,
max(case when rn=1 then bez_gem end) as bez_gem1,
max(case when rn=2 then bez_gem end) as bez_gem2,
max(case when rn=3 then bez_gem end) as bez_gem3,
max(case when rn=4 then bez_gem end) as bez_gem4,
max(case when rn=5 then bez_gem end) as bez_gem5,
... up to cnt ...
from(
select
landslide.lnumber,
lname,
bez_gem,
row_number() over(partition by landslide.lnumber) rn
from
<<some_data>>
) a
group by
landslide.lnumber,
lname
For your data and 5 possible values it would look like:
WITH double AS (
SELECT
partnumber, bez_gem
FROM
accumulation a, municipality b
WHERE
ST_Intersects(a.geom, b.geom)
AND EXISTS (
SELECT lnumber
FROM mun_more_than_once c
WHERE a.partnumber=c.lnumber)
ORDER BY
partnumber
)
select
landslide.lnumber,
lname,
max(case when rn=1 then bez_gem end) as bez_gem1,
max(case when rn=2 then bez_gem end) as bez_gem2,
max(case when rn=3 then bez_gem end) as bez_gem3,
max(case when rn=4 then bez_gem end) as bez_gem4,
max(case when rn=5 then bez_gem end) as bez_gem5
from (
select
landslide.lnumber,
lname,
bez_gem,
row_number() over(partition by landslide.lnumber) rn
from
double, landslide
where
double.partnumber=landslide.lnumber
) a
group by
landslide.lnumber,
lname