SQL transpose rows to columns and adding new variable [closed] - sql

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
How do I query this data using SQL:
to return this format, with NA for NULL

You can use PIVOT: -- I use MSSQL
SELECT DeptID,
[1] AS transaction_1,
[2] AS transaction_2,
[3] AS transaction_3
FROM
(
SELECT ID, transaction,
ROW_NUMBER() OVER(PARTITION BY ID ORDER BY (SELECT NULL)) AS rownum
FROM Your_Table) A
PIVOT
(
MAX(transaction) FOR rownum IN ([1],[2],[3])
)piv;

I assume you are using MSSql Server,
Example with sql query,
CREATE TABLE [dbo].[TransactionTable](
[User_ID] [int] NULL,
[TransactionAmount] [nvarchar](50) NULL
)
INSERT [dbo].[TransactionTable] ([User_ID], [TransactionAmount]) VALUES (1, 22)
GO
INSERT [dbo].[TransactionTable] ([User_ID], [TransactionAmount]) VALUES (1, 42)
GO
INSERT [dbo].[TransactionTable] ([User_ID], [TransactionAmount]) VALUES (1, 18)
GO
INSERT [dbo].[TransactionTable] ([User_ID], [TransactionAmount]) VALUES (2, 7)
GO
INSERT [dbo].[TransactionTable] ([User_ID], [TransactionAmount]) VALUES (2, 16)
GO
INSERT [dbo].[TransactionTable] ([User_ID], [TransactionAmount]) VALUES (3, 82)
GO
INSERT [dbo].[TransactionTable] ([User_ID], [TransactionAmount]) VALUES (3, 48)
GO
INSERT [dbo].[TransactionTable] ([User_ID], [TransactionAmount]) VALUES (3, 31)
To get your result,
SELECT [USER_ID]
,Transaction_1 = [1]
,Transaction_2 = [2]
,Transaction_3 = COALESCE([3] ,'NA')
FROM (
SELECT [user_id]
,[TransactionAmount]
,ROW_NUMBER() OVER(
PARTITION BY [user_id] ORDER BY(
SELECT NULL
)
) AS rownum
FROM TransactionTable
) TransactionTable
PIVOT(MIN([TransactionAmount]) FOR rownum IN ([1] ,[2] ,[3]))piv;
With renamed the column. It will definitely helpful to you.
Note: TransactionAmount column must be nvarchar.

Related

Find value contained in the HierarchyId at any level

I need to find a particular value contained in the SQL Server HierarchyId column. The value can occur at any level. Here is a sample code to illustrate the issue:
CREATE TABLE mytable
(
Id INT NOT NULL PRIMARY KEY,
TeamName VARCHAR(20) NOT NULL,
MyHierarchyId HIERARCHYID NOT NULL
);
INSERT INTO mytable(Id, TeamName, MyHierarchyId)
VALUES (1, 'Corporate','/1/');
INSERT INTO mytable(Id, TeamName, MyHierarchyId)
VALUES (2, 'Group A','/1/2/');
INSERT INTO mytable(Id, TeamName, MyHierarchyId)
VALUES (3, 'Team X','/1/2/3/');
INSERT INTO mytable(Id, TeamName, MyHierarchyId)
VALUES (4, 'Group B','/1/4/');
INSERT INTO mytable(Id, TeamName, MyHierarchyId)
VALUES (5, 'Team Y','/1/4/5/');
INSERT INTO mytable(Id, TeamName, MyHierarchyId)
VALUES (6, 'Team Z','/1/4/6/');
Now I would like to find all the records, which are associated with the Id = 4. This means records 4, 5 and 6. I could use a brute force methods like this:
SELECT [M].[Id],
[M].[TeamName],
[M].[MyHierarchyId],
[M].[MyHierarchyId].ToString() AS Lineage
FROM [dbo].[mytable] AS [M]
WHERE [M].[MyHierarchyId].ToString() LIKE '%4%'
But I suspect this will be very inefficient. Once again, the problem is that the level of the node I am searching for is not known in advance.
Thank you for any recommendations.
You can use IsDescendantOf()
Select *
from mytable
Where MyHierarchyID.IsDescendantOf( (select MyHierarchyID from mytable where id=4) ) = 1
Results
Id TeamName MyHierarchyId
4 Group B 0x5C20
5 Team Y 0x5C3180
6 Team Z 0x5C3280

SQL Filter rows based on multiple distinct values of a column

Given the following table
DECLARE #YourTable TABLE (id int, PLU int, Siteid int, description varchar(50))
INSERT #YourTable VALUES (1, 8972, 2, 'Beer')
INSERT #YourTable VALUES (2, 8972, 3, 'cider')
INSERT #YourTable VALUES (3, 8972, 4, 'Beer')
INSERT #YourTable VALUES (4, 8973, 2, 'Vodka')
INSERT #YourTable VALUES (5, 8973, 3, 'Vodka')
INSERT #YourTable VALUES (6, 8973, 4, 'Vodka')
I trying to write a query that would give me all rows that have multiple distinct values for a given description value against a plu.
So in the example above I would want to return rows 1,2,3 as they have both a 'cider' value and a 'beer' value for a plu of '8972'.
I thought 'GROUP BY' and 'HAVING' was the way to go but I can't seem to get it to work correctly.
SELECT P.PLU, P.Description
FROM #YourTable P
GROUP BY P.PLU, P.Description
HAVING COUNT(DISTINCT(P.DESCRIPTION)) > 1
Any help appreciated.
You shouldn't GROUP BY the description if you are doing a DISTINCT COUNT on it (then it will always be just 1). Try something like this:
SELECT P2.PLU, P2.Description
FROM #YourTable P2
WHERE P2.PLU in (
SELECT P.PLU
FROM #YourTable P
GROUP BY P.PLU
HAVING COUNT(DISTINCT(P.DESCRIPTION)) > 1
)

Return action items not in event table for all people - complex select where not exist

I feel like this should be an easy solve, but for I can't seem to solve this one.
Here are some sample tables and data.
CREATE TABLE [dbo].[people](
[PersonID] [int] NOT NULL,
[Name] [nvarchar](50) NOT NULL
) ON [PRIMARY]
INSERT INTO people (PersonID, Name) VALUES (1, 'Bob')
INSERT INTO people (PersonID, Name) VALUES (2, 'Bill')
INSERT INTO people (PersonID, Name) VALUES (3, 'Ben')
CREATE TABLE [dbo].[events](
[PersonID] [int] NOT NULL,
[ActionID] [int] NOT NULL,
[EventDate] [date] NOT NULL
) ON [PRIMARY]
-- Bob goes from sitting to running step by step
INSERT INTO events (PersonID, ActionID, EventDate) VALUES (1, 1, getdate()-3)
INSERT INTO events (PersonID, ActionID, EventDate) VALUES (1, 2, getdate()-2)
INSERT INTO events (PersonID, ActionID, EventDate) VALUES (1, 3, getdate()-1)
INSERT INTO events (PersonID, ActionID, EventDate) VALUES (1, 4, getdate())
-- Bill goes from sitting to walking, still waiting to run
INSERT INTO events (PersonID, ActionID, EventDate) VALUES (2, 1, getdate()-2)
INSERT INTO events (PersonID, ActionID, EventDate) VALUES (2, 2, getdate()-1)
INSERT INTO events (PersonID, ActionID, EventDate) VALUES (2, 3, getdate())
-- Ben manages to go from sitting to running without standing or walking
INSERT INTO events (PersonID, ActionID, EventDate) VALUES (3, 1, getdate()-3)
INSERT INTO events (PersonID, ActionID, EventDate) VALUES (3, 4, getdate())
CREATE TABLE [dbo].[actions](
[ActionID] [int] NOT NULL,
[Name] [nvarchar](50) NOT NULL
) ON [PRIMARY]
INSERT INTO actions (ActionID, Name) VALUES (1, 'Sit')
INSERT INTO actions (ActionID, Name) VALUES (2, 'Stand')
INSERT INTO actions (ActionID, Name) VALUES (3, 'Walk')
INSERT INTO actions (ActionID, Name) VALUES (4, 'Run')
What I'm trying to get is a list of all actions which do not have events for each user.
With the data above, I'd expect results like ...
|----------+----------|
| PersonID | ActionID |
|----------+----------|
| 2 | 4 |
|----------+----------|
| 3 | 2 |
|----------+----------|
| 3 | 3 |
|----------+----------|
I can do it for a specified person with something like ...
-- Get a list of actions that do not have an event for each user
SELECT
actions.ActionID,
events.PersonID
FROM
actions
LEFT OUTER JOIN events on events.ActionID = actions.ActionID
AND events.PersonID = 3
WHERE
events.PersonID IS NULL
But I'd really like to pull this out for all users without subqueries per user or cursors, etc.
The more I think about it, the less I think it can be done.
Any suggestions would be great.
Thanks. (SQL Fiddle)
Another approach can be using EXCEPT
SELECT PersonId, ActionId FROM [dbo].[people], [dbo].[actions]
EXCEPT
SELECT PersonId,ActionId FROM [dbo].[events]
Same using ANSI syntax
SELECT PersonId, ActionId FROM [dbo].[people] CROSS JOIN [dbo].[actions]
EXCEPT
SELECT PersonId,ActionId FROM [dbo].[events]
You can also do it using NOT EXISTS like following.
SELECT P.PersonId, A.ActionId FROM [dbo].[people] P CROSS JOIN [dbo].[actions] A
WHERE NOT EXISTS
(
SELECT 1 FROM [dbo].[events] E WHERE P.PersonID= E.PersonID AND A.ActionID= E.ActionID
)
Here is the solution to your problem:
SELECT p.PersonID,a.ActionID
FROM people p
CROSS JOIN actions a
LEFT JOIN Events e
ON p.PersonId = e.PersonID AND a.actionID = e.actionID
WHERE EventDate IS NULL;
Follow the link to the demo:
http://sqlfiddle.com/#!18/6b49b/19
Used Cross Join to generate all combinations of Person and Action
and then Used LEFT JOIN with Events to check which action is done by which person and selected those people and actions which didn't happen by using IS NULL.

SQL Server output in desired formatted way

I have two tables, tblName and tblCode.
tblName:
CREATE TABLE [dbo].[TblName]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NULL,
CONSTRAINT [PK_TblName]
PRIMARY KEY CLUSTERED ([Id] ASC)
) ON [PRIMARY]
tblCode:
CREATE TABLE [dbo].[tblCode]
(
[NameId] [int] NULL,
[Code] [varchar](50) NULL,
[Value] [varchar](50) NULL
) ON [PRIMARY]
Code:
INSERT INTO [dbo].[TblName]
([Name])
VALUES
('Rahul'),
('Rohit'),
('John'),
('David'),
('Stephen')
GO
INSERT INTO [dbo].[tblCode] ([NameId], [Code], [Value])
VALUES (1, 'DEL', 'Delivery'),
(1, 'DEL', 'Deployment'),
(2, 'REL', 'Release Management'),
(3, 'REL', 'Release Management'),
(4, 'TEST', 'Testing'),
(4, 'TEST', 'Final Testing')
I am trying to write a query to get all Names which are in tblCode with the Code and Value. For example I have NameId 1 present in tblCode with Code 'DEL' and Value as 'Delivery' and 'Deployment'. Similarly I have NameId 2,3 and 4 in tblCode with same or different Code and Value. So I am trying to get output in such a way if same name with same code is present in tblCode then it should come row with Name and Comma separated values as shown in below desired output.
This is they query I am using but its not giving the output I am looking for.
SELECT
N.Name,
CASE
WHEN C.Code = 'DEL'
THEN C.Value
ELSE ''
END As 'CodeValue'
FROM
TblName N
INNER JOIN
tblCode C ON N.Id = C.NameId
WHERE
C.NameId = 1 AND C.Code IN ('DEL', 'REL', 'TEST')
http://sqlfiddle.com/#!6/bdca78/11/0
CREATE TABLE tblName (id INTEGER, name VARCHAR(255));
INSERT INTO tblName VALUES(1, 'Rahul');
INSERT INTO tblName VALUES(2, 'Rohit');
INSERT INTO tblName VALUES(3, 'John');
INSERT INTO tblName VALUES(4, 'David');
INSERT INTO tblName VALUES(5, 'Steven');
CREATE TABLE tblCode(nameId INTEGER, code VARCHAR(255), value VARCHAR(255));
INSERT INTO tblCode VALUES(1, 'DEL', 'Delivery');
INSERT INTO tblCode VALUES(1, 'DEL', 'Development');
INSERT INTO tblCode VALUES(2, 'REL', 'Release Management');
INSERT INTO tblCode VALUES(3, 'REL', 'Release Management');
INSERT INTO tblCode VALUES(4, 'TEST', 'Testing');
INSERT INTO tblCode VALUES(4, 'TEST', 'Final Testing');
SELECT name,
codeValue
FROM
(SELECT tblName.name AS name,
STUFF((SELECT ',' + tblCode.value
FROM tblCode
WHERE tblCode.nameId = tblName.id
FOR XML PATH('')), 1 ,1, '') AS codeValue
FROM tblName) inline_view
WHERE codeValue IS NOT NULL;
Edit
ZoharPeled's solution is correct. This solution returns the name and a comma separated list of value and does not consider the code.
Zohar aggregates the list of value per code, which I think is what's required. OP, if the objective is to aggregate per name per code, including code in the output would make the result set more meaningful.
The following links show the difference, first is my SQL statement, then Zohar's.
My SQL: http://sqlfiddle.com/#!6/94fd0/1/0
Zohar's SQL: http://sqlfiddle.com/#!6/94fd0/2/0
Here is one way to do it:
;WITH CTE AS
(
SELECT DISTINCT
[NameId]
,[Code]
,(
SELECT STUFF(
(SELECT ',' + Value
FROM dbo.tblCode t1
WHERE t0.Code = t1.Code
AND t0.NameId = t1.NameId
FOR XML PATH(''))
, 1, 1, '')
) AS CodeValue
FROM dbo.tblCode t0
)
SELECT Name, CodeValue
FROM tblName
INNER JOIN CTE ON Id = CTE.NameId
ORDER BY Id
Results:
Name CodeValue
Rahul Delivery,Deployment
Rohit Release Management
John Release Management
David Testing,Final Testing
Read this SO post for an explanation on how to use STUFF and FOR XML to create a concatenated string from multiple rows.
You can see a live demo on rextester.

Oracle PIVOT, twice?

I have been trying to move away from using DECODE to pivot rows in Oracle 11g, where there is a handy PIVOT function. But I may have found a limitation:
I'm trying to return 2 columns for each value in the base table. Something like:
SELECT somethingId, splitId1, splitName1, splitId2, splitName2
FROM (SELECT somethingId, splitId
FROM SOMETHING JOIN SPLIT ON ... )
PIVOT ( MAX(splitId) FOR displayOrder IN (1 AS splitId1, 2 AS splitId2),
MAX(splitName) FOR displayOrder IN (1 AS splitName1, 2 as splitName2)
)
I can do this with DECODE, but I can't wrestle the syntax to let me do it with PIVOT. Is this even possible? Seems like it wouldn't be too hard for the function to handle.
Edit: is StackOverflow maybe not the right Overflow for SQL questions?
Edit: anyone out there?
From oracle-developer.net it would appear that it can be done like this:
SELECT somethingId, splitId1, splitName1, splitId2, splitName2
FROM (SELECT somethingId, splitId
FROM SOMETHING JOIN SPLIT ON ... )
PIVOT ( MAX(splitId) ,
MAX(splitName)
FOR displayOrder IN (1 AS splitName1, 2 as splitName2)
)
I'm not sure from what you provided what the data looks or what exactly you would like. Perhaps if you posted the decode version of the query that returns the data you are looking for and/or the definition for the source data, we could better answer your question. Something like this would be helpful:
create table something (somethingId Number(3), displayOrder Number(3)
, splitID Number(3));
insert into something values (1, 1, 10);
insert into something values (2, 1, 11);
insert into something values (3, 1, 12);
insert into something values (4, 1, 13);
insert into something values (5, 2, 14);
insert into something values (6, 2, 15);
insert into something values (7, 2, 16);
create table split (SplitID Number(3), SplitName Varchar2(30));
insert into split values (10, 'Bob');
insert into split values (11, 'Carrie');
insert into split values (12, 'Alice');
insert into split values (13, 'Timothy');
insert into split values (14, 'Sue');
insert into split values (15, 'Peter');
insert into split values (16, 'Adam');
SELECT *
FROM (
SELECT somethingID, displayOrder, so.SplitID, sp.splitname
FROM SOMETHING so JOIN SPLIT sp ON so.splitID = sp.SplitID
)
PIVOT ( MAX(splitId) id, MAX(splitName) name
FOR (displayOrder, displayOrder) IN ((1, 1) AS split, (2, 2) as splitname)
);