Pivoting rows into columns Oracle SQL - sql

I am currently working on a table where the format of the table is something like this (all the columns are of type VARCHAR except the INSERTDATE column which is of type DATE):
INSERTDATE | ID | PROPERTYNAME | PROPERTYVALUE
----------------------------------------------
date1 | 1000 | ItemNumber | 20.1A14
date1 | 1000 | ItemRev | 2
date1 | 1000 | BarCodeNumber | 3854981
date2 | 1001 | ItemNumber | 20.1B24
date2 | 1001 | ItemRev | 1
date2 | 1001 | BarCodeNumber | 3856539
What I want to do is to convert all PROPERTYNAME column values into separate columns with all of their respective PROPERTYVALUE column values into their respective columns, something like this:
INSERTDATE | ID | ItemNumber | ItemRev | BarCodeNumber
-------------------------------------------------------
date1 | 1000 | 20.1A14 | 2 | 3854981
date2 | 1001 | 20.1B24 | 1 | 3856539
I have been trying to solve this problem for days without any result. I looked up everything on Pivot on the internet but none of the examples match my own needs. I am not much familiar with Pivot in SQL so it would really be helpful if anyone can help me figure out how to use it to solve my problem.

If you know the columns you want, you can use conditional aggregation:
select insertdate, id,
max(case when PROPERTYNAME = 'ItemNumber' then propertyvalue end) as ItemNumber,
max(case when PROPERTYNAME = 'ItemRev' then propertyvalue end) as ItemRev,
max(case when PROPERTYNAME = 'BarCodeNumber' then propertyvalue end) as BarCodeNumber
from t
group by insertdate, id;
If you don't know all the properties up-front, then you need to construct the query dynamically as a string and use execute immediate.

The use case is a right candidate for PIVOT and you can use it perfectly here.
Assuming you are using Oracle as your database the query will look like,
select insertdate,id,ItemNumber,ItemRev,BarCodeNumber
from mytable
pivot
(
max(propertyvalue)
for propertyname in ('ItemNumber' as ItemNumber
,'ItemRev' ItemRev
,'BarCodeNumber' BarCodeNumber)
)
order by insertdate;

Related

How to count all values from a column, show values in that column and group by a date column?

I have a SQL database that has the follow columns: CreatedAt (datetime) and PlatformName (varchar)
| CreatedAt | PlatformName |
| -------- | -------------- |
| 2021/05/05 | bazinga.ar |
| 2021/06/06 | rammen.us |
| 2021/05/05 | iroko.it |
| 2021/06/06 | sundance.uk |
the relation is one to many (one date to many platformnames), i want to make a query that counts the PlatformNames, shows the distinct PlatformNames and groupby CreatedAt, like this
CreatedAt
Count(PlatformName)
PlatformName(without duplicates)
2021/06/02
45
name1, name2, name3
this is my query SELECT CreatedAt, COUNT(PlatformName) FROM database GROUP BY CreatedAt ORDER BY CreatedAt;
but i don't know how to show distinct platformnames, is it possible?
Since you seem unsure of which database platform you are using, the following works with SQL Server
select CreatedAt,
Count(*) CountOfPlatformName,
String_Agg(platformName,', ') PlatformNames
from t
group by createdAt
order by createdAt
Most database platforms have some sort of string aggregation, if you were using MySql it would be Group_concat, so ymmv, adjust as appropriate.

SQL displaying results as columns from rows

SQL Noob here.
I realize that many variations to this question have been asked but none seems to work or be fully applicable to my annoying situation, ie. I dont think PIVOT would work for what I require. I cant fathom the necessary words to google what I need efficiently.
I have the following query:
Select w.WORKORDERID, y.Answer
From
[SD].[dbo].[WORKORDERSTATES] w
LEFT JOIN [SD].[dbo].[WO_RESOURCES] x
ON w.workorderid = x.woid
Full Outer Join [SD].[dbo].ResourcesQAMapping y
ON x.UID = y.MAPPINGID
WHERE w.APPR_STATUSID = '2'
AND w.STATUSID = '1'
AND w.REOPENED = 'false'
It will bring back the following result:
+-----------+---------------------+
| WORKORDER | Answer |
+-----------+---------------------+
| 55693 | Brad Pitt |
| 55693 | brad.pitt#mycom.com |
| 55693 | Location |
| 55693 | NULL |
| 55693 | george |
+-----------+---------------------+
I would like all rows related to the value 55693 to output as columns like below:
+-----------+-----------+---------------------+----------+--------+--------+
| WORKORDER | VALUE1 | VALUE2 | VALUE3 | VALUE4 | VALUE5 |
+-----------+-----------+---------------------+----------+--------+--------+
| 55693 | Brad Pitt | brad.pitt#mycom.com | Location | NULL | george |
+-----------+-----------+---------------------+----------+--------+--------+
There will always be the same amount of values, and I am almost sure that the solution involves creating a temporary table but I cant get it to work any which way.
Any help would be greatly appreciated.
If you always have the same number of values (5) you can use a static PIVOT, otherwise you need a dynamic TSQL statement with PIVOT.
In both cases you'll need to add a column to guarantee rows/columns ordering otherwise there is no guarantee that you'll see the correct value in each column.
Here is a sample query thet uses a static PIVOT on 5 values (but remember to add a column to properly order the data replacing ORDER BY WORKORDER with ORDER BY YOUR_COLUMN_NAME):
declare #tmp table (WORKORDER int, Answer varchar(50))
insert into #tmp values
(55693, 'Brad Pitt')
,(55693, 'brad.pitt#mycom.com')
,(55693, 'Location')
,(55693, 'NULL')
,(55693, 'george')
select * from
(
select
WORKORDER,
Answer,
CONCAT('VALUE', ROW_NUMBER() OVER (PARTITION BY WORKORDER ORDER BY WORKORDER)) AS COL
from #tmp
) as src
pivot
(
max(Answer) for COL in ([VALUE1], [VALUE2], [VALUE3], [VALUE4], [VALUE5])
)
as pvt
Results:
Try to select another column that has different values as answer column and try to run pivot and that will work

How can I convert rows to columns in SQL

I've been referencing this question a lot, but my case is a little different so I haven't quite figured it out.
I have a set of data that looks something like this:
--------------------------------------
| Id | Answer| Question | EntryBy
--------------------------------------
| 1 |John |Name? | User1 |
| 2 |2.4 |Raiting? | User1 |
| 3 |ZH1E4A |UserId? | User1 |
| 4 |Paul |Name? | User1 |
| 5 |2.3 |Raiting? | User1 |
| 6 |Ron |Name? | User2 |
| 7 |857685 |UserId? | User2 |
----------------------------
I need to pivot the data so that it's structured like so:
----------------------------------------------------------
| Category | Name? | Raiting? | UserId? | EntryBy |
----------------------------------------------------------
| Category1| John | 2.4 | ZH1E4A | User1 |
| Category1| Paul | 2.3 | NULL | User1 |
| Category1| Ron | NULL | 857685 | User2 |
As you can see, there are multiple "Questions" but they don't always have an answer/value. I know the exact number of questions that may be asked/answered so I'm assuming that may help if I used a CASE expression?
Note: The 'Category' column in the last table is just another value similar to 'EntryBy' in the first. I've attempted the pivot approach in the cited question, but the results I get are not correct. I also tried the CASE statement but it resulted in an error since the Questions are titled the same.
Being 2008, we lose the sum() over function, but we can simulate it via a cross apply to create a Grp indicator.
This also assumes the ID is sequential (risky) and Name? is the Group Key.
Also, check the spelling of RAITING
Also, I have no idea where Category is coming from
Example
Select [Name?] = max(case when Question = 'Name?' then Answer end)
,[Raiting?] = max(case when Question = 'Raiting?' then Answer end)
,[UserId?] = max(case when Question = 'UserId?' then Answer end)
,[EntryBy?] = max([EntryBy])
From (
Select A.*
,B.Grp
From YourTable A
Cross Apply (Select Grp=count(*) from YourTable where Question='Name?' and ID<=A.ID) B
) A
Group By Grp
Returns
Name? Raiting? UserId? EntryBy?
John 2.4 ZH1E4A User1
Paul 2.3 NULL User1
Ron NULL 857685 User2
This only does a single parse of the table (or "Values Table Expression") for this one, compared to John's, which does 2:
WITH VTE AS (
SELECT *
FROM (VALUES
(1,'John ','Name? ','User1'),
(2,'2.4 ','Raiting?','User1'),
(3,'ZH1E4A','UserId? ','User1'),
(4,'Paul ','Name? ','User1'),
(5,'2.3 ','Raiting?','User1'),
(6,'Ron ','Name? ','User2'),
(7,'857685','UserId? ','User2'),
(8,'Steve ','Name? ','User3'),
(9,'2.5 ','Raiting?','User3'),
(10,'Jane ','Name? ','User3'),
(11,'GA18S1','UserId? ','User3'),
(12,'2.3 ','Raiting?','User3'),
(13,'ABH12D','UserId? ','User3')) V(ID, Answer, Question, EntryBy)),
Groups AS(
SELECT *,
ROW_NUMBER() OVER (ORDER BY ID ASC) -
ROW_NUMBER() OVER (PARTITION BY CASE WHEN Question = 'Name?' THEN 0 ELSE 1 END ORDER BY ID ASC) AS Grp
FROM VTE)
SELECT 'Category1' AS Category,
MAX(CASE Question WHEN 'Name?' THEN Answer ELSE NULL END) AS [Name?],
MAX(CASE Question WHEN 'Raiting?' THEN Answer ELSE NULL END) AS [Raiting?],
MAX(CASE Question WHEN 'UserID?' THEN Answer ELSE NULL END) AS [UserID?],
EntryBy
FROM Groups
GROUP BY CASE Grp WHEN 0 THEN Grp + 1 ELSE Grp END,
EntryBy
ORDER BY CASE Grp WHEN 0 THEN Grp + 1 ELSE Grp END;
I also added a few extra values to display what happens if the sequencing goes wrong.
Result set:
Category Name? Raiting? UserID? EntryBy
--------- ------- -------- ------- -------
Category1 John 2.4 ZH1E4A User1
Category1 Paul 2.3 NULL User1
Category1 Ron NULL 857685 User2
Category1 Steve 2.5 NULL User3
Category1 Jane 2.3 GA18S1 User3

get 2 column values to 2 columns for 1 row (or comma-separated)

I have a problem where I have to alter another programmer's very large SQL query.
My problem is described by this table:
+--------+-----------+---------------+-------------------------+
| Id | ProductId | Barcode | CreatedAt |
+--------+-----------+---------------+-------------------------+
| 30665 | 312118 | 4054065383840 | 2017-03-13 18:37:13.130 |
| 128600 | 312118 | 4054065383857 | 2017-05-22 13:26:48.683 |
+--------+-----------+---------------+-------------------------+
So as you can see, a product has 2 barcodes. In our query, I need to display these 2 barcodes in 2 columns, instead of 2 different rows, as barcode1 and barcode2, or some such.
SELECT ISNULL(pp.Barcode,'') AS BarCode
FROM (...) c
LEFT JOIN ProductBarcode pp on pp.ProductId=c.VariantProductId
This is the query in use atm.
Hopefully I explained it well enough.
Thanks in advance,
Rasmus.
EDIT: db is MSSQL
Use pivot query , much effective in this scenario. Use more row numbers if there are more than 2 barcodes as barcode1,barcode2,barcode3
SELECT productid
,[1] AS Barcode1
,[2] AS Barcode2
FROM (
SELECT productid
,barcode
,ROW_NUMBER() OVER (
PARTITION BY productid ORDER BY barcode
) rn
FROM #mytable
) my
pivot(max(barcode) FOR rn IN ([1], [2])) AS pvt

Count and name content from a SQL Server table

I have a table which is structured like this:
+-----+-------------+-------------------------+
| id | name | timestamp |
+-----+-------------+-------------------------+
| 1 | someName | 2016-04-20 09:41:41.213 |
| 2 | someName | 2016-04-20 09:42:41.213 |
| 3 | anotherName | 2016-04-20 09:43:41.213 |
| ... | ... | ... |
+-----+-------------+-------------------------+
Now, I am trying to create a query, which selects all timestamps since time x and count the amount of times the same name occurs in the result.
As an example, if we would apply this query to the table above, with 2016-04-20 09:40:41.213 as the date from which on it should be counted, the result should look like this:
+-------------+-------+
| name | count |
+-------------+-------+
| someName | 2 |
| anotherName | 1 |
+-------------+-------+
What I have accomplished so far is the following query, which gives me the the names, but not their count:
WITH screenshots AS
(
SELECT * FROM SavedScreenshotsLog
WHERE timestamp > '2016-04-20 09:40:241.213'
)
SELECT s.name
FROM SavedScreenshotsLog s
INNER JOIN screenshots sc ON sc.name = s.name AND sc.timestamp = s.timestamp
ORDER BY s.name
I have browsed through stackoverflow but was not able to find a solution which fits my needs and as I am not very experienced with SQL, I am out of ideas.
You mention one table in your question, and then show a query with two tables. That makes it hard to follow the question.
What you are asking for is a simple aggregation:
SELECT name, COUNT(*)
FROM SavedScreenshotsLog
WHERE timestamp > '2016-04-20 09:40:241.213'
GROUP BY name
ORDER BY COUNT(*) DESC;
EDIT:
If you want "0" values, you can use conditional aggregation:
SELECT name,
SUM(CASE WHEN timestamp > '2016-04-20 09:40:241.213' THEN 1 ELSE 0 END) as cnt
FROM SavedScreenshotsLog
GROUP BY name
ORDER BY cnt DESC;
Note that this will run slower because there is no filter on the dates prior to aggregation.
CREATE TABLE #TEST (name varchar(100), dt datetime)
INSERT INTO #TEST VALUES ('someName','2016-04-20 09:41:41.213')
INSERT INTO #TEST VALUES ('someName','2016-04-20 09:41:41.213')
INSERT INTO #TEST VALUES ('anotherName','2016-04-20 09:43:41.213')
declare #YourDatetime datetime = '2016-04-20 09:41:41.213'
SELECT name, count(dt)
FROM #TEST
WHERE dt >= #YourDatetime
GROUP BY name
I've posted the answer, because using the above query can generate errors in converting the string in where clause into a datetime, it depends on the format of the datetime.