Is it possible to do pivot query in ABAP CDS query? Is there a way we can achieve this like what we are always doing in SQL server and MySQL?
No, currently it is not supported natively neither in ABAP CDS nor in HANA.
There are several workarounds you can use to simulate pivot behavior:
Pivoting with COUNT and UNION ALL [sample]
select vendor_id, sum("E1") AS "E1", SUM("E2") AS "E2", SUM("E3") AS "E3"
FROM
(
select vendor_id, COUNT(NUM) as "E1", 0 AS "E2" , 0 AS "E3"
from "ABHISHEAGRAW".
WHERE EMP_ID = 'E1' GROUP BY vendor_id
union all
select vendor_id, 0 AS “E1”, COUNT(NUM) as "E2", 0 AS "E3"
from "ABHISHEAGRAW".
WHERE EMP_ID = 'E2' GROUP BY vendor_id
union all
select vendor_id, 0 AS “E1”, 0 as "E2", COUNT(NUM) AS "E3"
from "ABHISHEAGRAW".
WHERE EMP_ID = 'E3' GROUP BY vendor_id
) GROUP BY vendor_id ORDER BY vendor_id
Pivoting with COUNT through CASE [sample]
select vendor_id, sum("E1") AS "E1", SUM("E2") AS "E2", SUM("E3") AS "E3"
FROM
(
select vendor_id, CASE (EMP_ID) WHEN 'E1'
THEN 1
ELSE 0
END as "E1",
CASE (EMP_ID) WHEN 'E2'
THEN 1
ELSE 0
END as "E2",
CASE (EMP_ID) WHEN 'E3'
THEN 1
ELSE 0
END as "E3"
from “ABHISHEAGRAW”.
)GROUP BY vendor_id ORDER BY vendor_id
Pivoting with CASE and grouping [sample]
The given snippets are HANA examples but they can be adapted to ABAP CDS. If you give a MCVE of your case, we can propose you a possible solution.
Related
I have a table with 3 columns id, homeph, mobileph.
If homeph is equal to mobileph, then homeph or mobileph with other line what is query for this?
This should give you the desired result.
SELECT ID AS id, Homeph AS phone
FROM table
UNION
SELECT ID AS id, Contactph AS phone
FROM table
In Oracle, you could use:
SELECT DISTINCT id, phone
FROM input_table
UNPIVOT (
phone FOR type IN (homeph, contactph)
)
But it will be more efficient to check if the phone numbers are identical before unpivoting (as UNPIVOT will, by default, ignore NULL values):
SELECT id, phone
FROM (
SELECT id,
homeph,
CASE WHEN homeph = contactph THEN NULL ELSE contactph END AS contactph
FROM input_table
)
UNPIVOT (
phone FOR type IN (homeph, contactph)
)
Which, for the sample data:
CREATE TABLE input_table (id, homeph, contactph ) AS
SELECT 1, 99999, 88888 FROM DUAL UNION ALL
SELECT 2, 77777, 77777 FROM DUAL;
Both output:
ID
PHONE
1
99999
1
88888
2
77777
fiddle
I have a piece of code that I would like to turn into a function. The purpose of the code is to group certain records together on variable criteria and create a grouped concatenation using STUFF(). I would like to be able to toggle the parameters on which the group by happens (and therefore also the parameters for the STUFF).
However, the following gives me errors where the optional parameters (e.g. OwnerName in the example below) are invalid in the select list because they are not contained in either an aggregation function or the GROUP BY clause.
Consider a simplified example like the below (the real life version has a lot of parameters, hence why I would like to be able to get these all into one query):
SELECT CarMake, CarModel, CASE WHEN #FlagOwnerName = 1 THEN OwnerName ELSE NULL END AS [OwnerName], SUM(CarValue),
LicenseIDs = STUFF((SELECT ',' + CONVERT(VARCHAR(20),Cars2.LicenseID) AS [text()]
FROM DB.dbo.Cars Cars2
WHERE Cars2.CarMake = Cars1.CarMake
AND Cars2.CarModel = Cars1.CarModel
AND (#FlagOwnerName = 0 OR Cars2.OwnerName = Cars1.OwnerName)
FOR XML PATH('')), 1, 1, '')
FROM DB.dbo.Cars Cars1
GROUP BY CarMake,
CarModel,
CASE WHEN #FlagOwnerName = 1 THEN OwnerName ELSE NULL END
EDIT: if I change the below, then it 'seems' to return the correct concatenation except if it is NULL, then the concatenation is NULL itself. Additionally, if I try to change the values to ISNULL(Cars1.OwnerName, 'Placeholder') or similarly with COALESCE, it gives me the same error (not valid in select statement as above).
AND (#FlagOwnerName = 0 OR Cars2.OwnerName = Cars1.OwnerName)
to
AND CASE WHEN #FlagOwnerName = 1 THEN Cars1.OwnerName = Cars2.OwnerName
Based on your comments, I don't think using STUFF with FOR XML if the best way to approach this. Typically the best way to concatenate multiple rows into a single string is to use a recursive Common Table Expression (CTE).
There are some examples of using CTEs (and some alternative approaches) here.
I've adapted one of the CTE options there to do something similar to what you've described.
First, I've set up a simple table that resembles the data you've described:
create table #cars (CarMake varchar(50), CarModel varchar(50), CarValue INT, OwnerName varchar(50), LicenseID varchar(50));
insert into #cars(CarMake, CarModel, CarValue, OwnerName, LicenseID) values ('Toyota','Camry', 12000, 'Steve','ABC123');
insert into #cars(CarMake, CarModel, CarValue, OwnerName, LicenseID) values ('Toyota','Camry', 12000, 'Bob','HED999');
insert into #cars(CarMake, CarModel, CarValue, OwnerName, LicenseID) values ('Toyota','Camry', 19000, 'Helen','WKS444');
insert into #cars(CarMake, CarModel, CarValue, OwnerName, LicenseID) values ('Ford','Mustang',30000, 'Amy','JKJL88');
insert into #cars(CarMake, CarModel, CarValue, OwnerName, LicenseID) values ('Ford','Mustang',30000, 'Billy-Bob','EZ1111');
insert into #cars(CarMake, CarModel, CarValue, OwnerName, LicenseID) values ('Aston Martin','Vantage',90000, 'Mike','HY7733');
I've then used a CTE to build a dataset where the car licenses and values are appended/aggregated by make/model. The variable #FlagOwnerName controls whether these values from the CTE or the base values from the source table are used in the final SELECT statement:
DECLARE #FlagOwnerName bit = 1;
WITH cte (CarMake, CarModel, CarValueTotal, Car_Val, LicenseList, License_ID, length_)
AS
(
SELECT
CarMake, CarModel, 0, 0, CAST( '' AS VARCHAR(8000) ), CAST( '' AS VARCHAR(8000) ), 0
FROM #cars
GROUP BY CarMake, CarModel
UNION ALL
SELECT c.CarMake, c.CarModel, cte.CarValueTotal + c.CarValue, c.CarValue,
CAST(cte.LicenseList + CASE WHEN length_ = 0 THEN '' ELSE ', ' END + c.LicenseID AS VARCHAR(8000) ),
CAST( LicenseID AS VARCHAR(8000)),
length_ + 1
FROM cte
INNER JOIN #cars c ON cte.CarMake = c.CarMake AND cte.CarModel = c.CarModel
WHERE c.LicenseID > cte.License_ID
)
SELECT
cars.CarMake,
cars.CarModel,
CASE WHEN #FlagOwnerName = 1 THEN cars.OwnerName ELSE 'ALL' END as OwnerName,
CASE WHEN #FlagOwnerName = 1 THEN cars.CarValue ELSE totals.CarValueTotal END as CarValue,
CASE WHEN #FlagOwnerName = 1 THEN cars.LicenseID ELSE totals.LicenseList END as LicenseID
FROM #cars cars
INNER JOIN
(
SELECT CarMake, CarModel, LicenseList, CarValueTotal
FROM (
SELECT CarMake, CarModel, LicenseList, CarValueTotal,
RANK() OVER ( PARTITION BY CarMake, CarModel ORDER BY length_ DESC )
FROM CTE
) D ( CarMake, CarModel, LicenseList, CarValueTotal, rank )
WHERE rank = 1
) totals ON cars.CarMake = totals.CarMake AND cars.CarModel = totals.CarModel
GROUP BY
cars.CarMake,
cars.CarModel,
CASE WHEN #FlagOwnerName = 1 THEN cars.OwnerName ELSE 'ALL' END,
CASE WHEN #FlagOwnerName = 1 THEN cars.CarValue ELSE totals.CarValueTotal END,
CASE WHEN #FlagOwnerName = 1 THEN cars.LicenseID ELSE totals.LicenseList END
So when #FlagOwnerName = 1 we get:
CarMake CarModel OwnerName CarValue LicenseID
Aston Martin Vantage Mike 90000 HY7733
Ford Mustang Amy 30000 JKJL88
Ford Mustang Billy-Bob 30000 EZ1111
Toyota Camry Bob 12000 HED999
Toyota Camry Helen 19000 WKS444
Toyota Camry Steve 12000 ABC123
And when #FlagOwnerName = 0 we get:
CarMake CarModel OwnerName CarValue LicenseID
Aston Martin Vantage ALL 90000 HY7733
Ford Mustang ALL 60000 EZ1111, JKJL88
Toyota Camry ALL 43000 ABC123, HED999, WKS444
Note that in your comment you implied you didn't want OwnerName to be returned when #FlagOwnerName = 0, while this is possible inside a stored procedure (i.e. executing different queries based on a parameter) I wouldn't recommend it. Better to have a consistent set of columns returned, if you're using a reporting tool over the top of it then you can potentially include some logic there to hide the column based on the parameter value.
Version: Microsoft SQL Server 2014 - 12.0.2000.8 (X64) Feb 20 2014 20:04:26 Copyright (c) Microsoft Corporation Express Edition (64-bit) on Windows NT 6.1 (Build 7601: Service Pack 1)
I need to select a varying number of values from a table where a certain column is equal to a parameter and a certain column is LIKE 'String1' or 'String2'.
I have created a stored procedure that is returning the MAX and MIN strings, but naturally this method is not dynamic.
I have tried the following query which says it completes successfully, but does not return any results.
SELECT UPC, PartNum, PartDesc
FROM dbo.table
WHERE UPC = #upc
GROUP BY UPC, PartNum, PartDesc
HAVING PartDesc in ('%RED%','%BLUE%')
ORDER BY PartDesc;
Example table:
ID UPC PartNum PartDesc
-------------------------------------------
1 123 543 Red1
2 123 345 Blue1
3 123 654 Red2
4 123 765 Blue2
I need to pass a parameter to a stored procedure as #upc from an application.
Where it will find any PartDesc that are like '%RED%' or '%BLUE%' AND where the UPC = #upc.
Then store the Part#(s) found in a new table to be queried later.
Created Table From Stored Procedure:
ID UPC Red1 Red2 Blue1 Blue2
----------------------------------------------------------
1 123 543 654 345 765
There can be any number or combination of "Red" or "Blue" per UPC number. i.e.,
Some UPC numbers may only have two "Red" parts and one "Blue" part and others may only have two "Red" parts and no "Blue" parts. Maybe five "Red" parts and ten "Blue" parts.
How do would I write the query that will store the varying number of found results to a new table in a stored procedure?
Edit
It seems as though the PIVOT function should be used, but I am unsure of how to use the required aggregate in my scenario. I don't need to pivot on the "SUM" of PartDesc or any other column for that matter.
Perhaps a dynamic Pivot?
EDIT Based on Corgi's recommendation. Also, showing my work.
DECLARE #upc As varchar(13)
DECLARE #Red1 As nvarchar(100) = CASE
WHEN
(
SELECT MIN(PartNum) FROM dbo.table
WHERE PartDesc LIKE '%RED%' AND UPC = #upc
) IS NOT NULL THEN
(
SELECT MIN(PartNum) FROM dbo.table
WHERE PartDesc LIKE '%RED%' AND UPC = #upc
)
ELSE 'Not Found'
END
DECLARE #Red2 As nvarchar(100) = CASE
WHEN
(
SELECT MAX(PartNum) FROM dbo.table
WHERE PartDesc LIKE '%RED%' AND UPC = #upc
) IS NOT NULL THEN
(
SELECT MAX(PartNum) FROM dbo.table
WHERE PartDesc LIKE '%RED%' AND UPC = #upc
)
ELSE 'Not Found'
END
DECLARE #Blue1 As nvarchar(100) = CASE
WHEN
(
SELECT MAX(PartNum) FROM dbo.table
WHERE PartDesc LIKE '%BLUE%' AND UPC = #upc
) IS NOT NULL THEN
(
SELECT MAX(PartNum) FROM dbo.table
WHERE PartDesc LIKE '%BLUE%' AND UPC = #upc
)
ELSE 'Not Found'
END
;WITH MostColumns AS
(
SELECT UPC, #Red1 As Part1, #Red2 As Part2, #Blue1 As Part3
FROM (SELECT UPC, PartNum, PartDesc
FROM dbo.table) AS source
PIVOT
(MIN(PartNum) FOR PartDesc IN ([Part1], [Part2], [Part3])) AS pvt
)
SELECT MIN(p.ID) AS ID, p.UPC, mc.Part1, mc.Part2, mc.Part3
INTO MyNewTable
FROM dbo.table p
INNER JOIN MostColumns mc ON p.UPC = mc.UPC
GROUP BY p.UPC, mc.Part1, mc.Part2, mc.Part3
Result:
ID UPC Part1 Part2 Part3
2876 123 Not Found Not Found Not Found
2758 213 Not Found Not Found Not Found
2321 312 Not Found Not Found Not Found
802 321 Not Found Not Found Not Found
868 132 Not Found Not Found Not Found
This is the correct format, but no cigar. I know for a fact, that all of my UPCs contain atleast one Red1 part. For some reason, it did not find any of the parts.
EDIT--ANSWER
#Corgi After more research on dynamic pivots I arrived at this solution. I will still need to build on it to make it operate the way I need it to. Although, those are not relevant to this question.
Thank you #bluefeet for your answer in this post.
SQL Dynamic Pivot
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ','
+ QUOTENAME('Part_' + cast(rn as varchar(10)))
from dbo.table
cross apply
(
select row_number() over(partition by UPC order by PartNum) rn
from dbo.table
) x
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT UPC, ' + #cols + ' from
(
select UPC, PartNum,
''Component_''
+ cast(row_number() over(partition by UPC order by PartNum) as varchar(10)) val
from dbo.table
) x
pivot
(
max(PartNum)
for val in (' + #cols + ')
) p '
execute(#query)
Your observation about PIVOT is on track, but you can't actually use PIVOT without specifying the output column names (that is, the values from PartDesc). It sounds like, because there can be a varying number of these PartDesc values, the closest you can get would be to find all the values:
SELECT DISTINCT t.PartDesc
FROM MyTable t
WHERE t.PartDesc LIKE '%Red%' OR t.PartDesc LIKE '%Blue%'
Then you can use the values to build your query. If you really have to have the query be dynamic, you would need to construct a query string to use with something like sp_executesql. The way you would create the table from the output is by using SELECT... INTO within your dynamic query.
The PIVOT syntax you need, combined with SELECT... INTO, might look something like:
;WITH MostColumns AS
(
SELECT UPC, Red1, Red2, Blue1, Blue2
FROM (SELECT UPC, PartNum, PartDesc
FROM dbo.table) AS source
PIVOT
(MIN(PartNum) FOR PartDesc IN ([Red1], [Red2], [Blue1], [Blue2])) AS pvt
)
SELECT MIN(p.ID) AS ID, p.UPC, mc.Red1, mc.Red2, mc.Blue1, mc.Blue2
INTO MyNewTable
FROM dbo.table p
INNER JOIN MostColumns mc ON p.UPC = mc.UPC
GROUP BY p.UPC, mc.Red1, mc.Red2, mc.Blue1, mc.Blue2
The MostColumns common table expression is there because it doesn't work well to have ID in your original query -- it's an "extra" column that is not part of the pivot.
I have a table name #Table1(See the attachment) I want following out put (See the attachment)
#Raging Bull's answer is correct. Here is version using PIVOT
SELECT FormatType, [True], [False], [Blank], [True] + [False] + [Blank] AS Total
FROM
(
SELECT FormatType, Result
FROM Table1
) AS SourceTable
PIVOT
(
COUNT(Result)
FOR Result IN ([True], [False], [Blank])
) AS PivotTable
It produces the exact same result.
See result in SQL Fiddle
Try this:
SELECT FormatType,
ISNULL(COUNT(CASE WHEN Result='True' THEN '1' END),0) AS [True],
ISNULL(COUNT(CASE WHEN Result='False' THEN '1' END),0) AS [False],
ISNULL(COUNT(CASE WHEN Result='Blank' THEN '1' END),0) AS [Blank],
ISNULL(COUNT(1),0) AS [Total]
FROM Table1
GROUP BY FormatType
ORDER BY FormatType DESC
Explanation:
This query will select the FormatType along the count of each cases and the total. ISNULL is used for replacing NULL values with 0 (in case of FALSE in ASP).
Result:
FORMATTYPE TRUE FALSE BLANK TOTAL
PSP 1 2 1 4
ASP 1 0 2 3
See result in SQL Fiddle.
"Need to display all items linked to the parent category id=1 As per the table, It should fetch:Big Machine, Computer, CPU Cabinet, Hard Disk and Magnetic Disk. But by the logic that is written it is not fetching all the records. Plz help.."
create table ItemSpares
(
ItemName varchar(20),
ItemID int,
ParentCategoryID int
)
insert into ItemSpares (ItemName,ItemID,ParentCategoryID)
select 'Big Machine', 1 , NULL UNION ALL
select 'Computer', 2, 1 UNION ALL
select 'CPU Cabinet', 3, 2 UNION ALL
select 'Hard Disk', 4, 3 UNION ALL
select 'Magnetic Disk',5,4 UNION ALL
select 'Another Big Machine',6, NULL
You need to use a hierarchical SQL query, took a while to figure out but try this:
with BigComputerList (ItemName, ItemID, ParentCategoryID, Level)
AS
(
-- Anchor member definition
SELECT e.ItemName, e.ItemID, e.ParentCategoryID,
0 AS Level
FROM ItemSpares AS e
WHERE ItemID = 1
UNION ALL
-- Recursive member definition
SELECT e.ItemName, e.ItemID, e.ParentCategoryID,
Level + 1
FROM ItemSpares AS e
INNER JOIN BigComputerList AS d
ON e.ParentCategoryId = d.ItemID
)
Select * From BigComputerList
I would highly recommend reading this article if you want to understand what the query is doing.