I'm trying to use the pivot function in SQL Server to transform some results, but I'm running into trouble.
The table only has 2 columns, which look like this:
company category
----- -----
company 1 Arcade
company 1 Action
company 2 Arcade
company 2 Adventure
I'd like to transform it to this:
company category 1 category 2
----- ----- -----
company 1 Arcade Action
company 2 Arcade Adventure
So far all I can find are examples of pivot functions where there is a 3rd column in the original results with "category 1" or "category 2", which then uses the values in those column as the names of the new, pivoted columns.
What I want to do is simply define the names of the columns from scratch. Is there a way to do this with the pivot function?
Thanks in advance!
Since you need a third column that contains category1, category2, etc, then I would suggest applying a windowing function like row_number() to your data first before attempting to convert the data into columns. The row_number() function will create a unique sequenced number for each company and category, you will then use this calculated value to pivot the data.
The easiest way to convert the data would be to use an aggregate function along with a CASE expression. First, you will use a subquery to generate the row_number():
select company,
max(case when seq = 1 then category end) Category1,
max(case when seq = 2 then category end) Category2
from
(
select company, category,
row_number() over(partition by company
order by company) seq
from yourtable
) d
group by company;
See SQL Fiddle with Demo.
Now, if you want to use the PIVOT function you would still use the row_number(), but you would place the new calculated sequence as the new column names:
select company, category1, category2
from
(
select company, category,
'category'+
cast(row_number() over(partition by company
order by company) as varchar(10)) seq
from yourtable
) d
pivot
(
max(category)
for seq in (Category1, Category2)
) piv;
See SQL Fiddle with Demo. These generate a result of:
| COMPANY | CATEGORY1 | CATEGORY2 |
|-----------|-----------|-----------|
| company 1 | Arcade | Action |
| company 2 | Arcade | Adventure |
Related
I am trying to place all bought items of one order in one record. Something like this
OrderID Occupation Age Product Name 1 Product Name 2
300 Network Analyst 33 Switches Hp Laptop
For now, my records look like this
OrderID Occupation Age Product Name
300 Network Analyst 33 Switches
300 Network Analyst 33 Hp Laptop
Initially I have four tables. The Customer Table, Product Table, Order Table and Ordered Product Table.
I created a view of all the transactions, which basically looks like the second table shown above.
I want it to look like the first table above if possible.
Use conditional aggregation :
select OrderId, Occupation, Age,
max(case when Seq = 1 then Product end) [Product1],
max(case when Seq = 2 then Product end) [Product2]
from (select *,
row_number() over (partition by OrderId, Occupation, Age order by Product) Seq
from table t) t1
where Seq in (1,2)
group by OrderId, Occupation, Age;
However, this may fail if you are looking n number of product IF so, then you need to go with PIVOT with dynamic style.
Is it possible to have SQL Server Management Studio automatically generate column names when none is specified? For instance based on the expression generating them.
For instance, it would be nice to have the query
SELECT ProductType, AVG(Price)
FROM Products
GROUP BY ProductType
generate something like
ProductType | AVG(Price)
------------+-----------
Product1 | 42
... | ...
rather than
ProductType | (No column name)
------------+-----------------
Product1 | 42
... | ...
as it currently does.
SQL Server (Transact-SQL) ALIASES can be used to create a temporary name for columns or tables.
SELECT ProductType, AVG(Price) as AVG
FROM Products
GROUP BY ProductType
https://www.techonthenet.com/sql_server/alias.php
You can name a new column in '' too. For example you can write:
SELECT ProductType, AVG(Price) as 'AVG(Price)'
FROM Products
GROUP BY ProductType
I really hope you can help me.
I have this table: Text
Text | LANGUAGECODE | PRODUCT
BlaENU | ENU | PROD1
BlaDAN | DAN | PROD1
BlaENU | ENU | PROD2
I need a SQL line, where I chan check if language code DAN exist, then use DAN, else USE ENU. How can i do that?
Right now i have lines SQL, but i get both DAN and ENU text out.
SELECT *
FROM Text
WHERE PRODUCT = 'PROD1' AND (LANGUAGECODE = 'DAN' OR LANGUAGECODE = 'ENU')
Can anybody help me with my SQL statement.
You can use ROW_NUMBER for this:
SELECT Text, LANGUAGECODE, PRODUCT
FROM (
SELECT Text, LANGUAGECODE, PRODUCT,
ROW_NUMBER() OVER (PARTITION BY PRODUCT
ORDER BY CASE
WHEN LANGUAGECODE='DAN' THEN 1
ELSE 2
END) AS rn
FROM Text
WHERE LANGUAGECODE IN ('DAN', 'ENU')) AS t
WHERE t.rn = 1
The PARTITION BY clause creates one row-number slice per unique product. The ORDER BY clause prioritizes DAN code over ENU. The outer query picks DAN rows where they exist, otherwise it selects ENU records.
I want to use an aggregate function in SQL Server to sum the number of seats:
The table is like that (it's all the same software just version changes)
OrderID | CustomerID | ProductID | Product Name | No of Seats
1 | 11 | 351-0212-4 | soft v601,Download | 3
2 | 11 | 361-0313-5 | soft v701,Upgrade | 2
3 | 12 | 341-1210-4 | soft v501,Download | 5
4 | 12 | 351-0212-5 | soft v601,Upgrade | 2
...
And I want a result like
Sum(no of seats)
8
So If a customer already bought the software but have upgraded keep number of seats for the customer.
e.g.:
Customer 11 bought 3 licences of our soft and then he bought two upgrades of a newer vesion so the sum for him should be 3 instead of 5.
Is that something possible to do in SQL ?
I hope I've been clear if not let me know.
Thanks in advance.
something like
select CustomerID, sum([No of Seats])
from <your table>
where [Product Name] not like '%upgrade%'
group by CustomerID
But in general - filter out those you don't want to see in the results and then sum. And if you want total number (not per customer):
select sum([No of Seats])
from <your table>
where [Product Name] not like '%upgrade%'
you should add boolean column, like 'isActive', then your select can be like this
select customerid, sum(numberOfSeats) from table
where isActive = 1
group by customerid
You have some normalization problems. The Product Name column is (presumably) redundant with the ProductID column, plus Product Name
apparently carries two logically distinct pieces of information: the name itself, and whether that product is an upgrade.
It would be better to split this into two tables, say Products and Orders. The Products table would have columns ProductID (primary key), Product_Name, and Is_Upgrade, and the Orders table would have columns OrderID (primary key), CustomerID (foreign key), ProductID (foreign key), and NumberOfSeats.
Given what you now have, however, and assuming that you want to avoid counting seats where the product name ends in 'Upgrade', you seem to want a query along these lines:
SELECT SUM("No of seats")
FROM Orders
WHERE CustomerID = 11 AND "Product Name" NOT LIKE '%Upgrade'
Lets say that I have to store the following information in my database,
Now my database tables will be designed and structured like this,
In a later date, if I had to add another sub category level how will I be able to achieve this without having to change the database structure at all?
I have heard of defining the columns as row data in a table and using pivots to extract the details later on...Is that the proper way to achieve this?
Can someone please enlighten me or guide me in the proper direction? Thanks in advance...
:)
It would be difficult to add more columns to your table when new levels are to be generated. The best way is to use a Hierarchy table to maintain Parent-Child relationship.
Table : Items
x----x------------------x------------x
| ID | Items | CategoryId |
|----x------------------x------------x
| 1 | Pepsi | 3 |
| 2 | Coke | 3 |
| 3 | Wine | 4 |
| 4 | Beer | 4 |
| 5 | Meals | 2 |
| 6 | Fried Rice | 2 |
| 7 | Black Forest | 7 |
| 8 | XMas Cake | 7 |
| 9 | Pinapple Juice | 8 |
| 10 | Apple Juice | 8 |
x----x------------------x------------x
Table : Category
In category table, you can add categories to n levels. In Items table, you can store the lowest level category. For example, take the case of Pepsi - its categoryId is 3. In Category table, you can find its parent using JOINs and find parent's parents using Hierarchy queries.
In Category table, the categories with ParentId is null(that is with no parentId) will be the MainCategory and the other items with ParentId will be under SubCategory.
EDIT :
Any how you need to alter the tables, because as per your current schema, you cannot add column to the first table because the number of Sub category may keep on changing. Even if you create a table as per Rhys Jones answer, you have to join two tables with string. The problem in joining with string is that, when there is a requirement to change the Sub category or Main category name, you have to change in every table which you be fall to trouble in future and is not a good database design. So I suggest you to follow the below pattern.
Here is the query that get the parents for child items.
DECLARE #ITEM VARCHAR(30) = 'Black Forest'
;WITH CTE AS
(
-- Finds the original parent for an ITEM ie, Black Forest
SELECT I.ID,I.ITEMS,C.CategoryId,C.Category,ParentId,0 [LEVEL]
FROM #ITEMS I
JOIN #Category C ON I.CategoryId=C.CategoryId
WHERE ITEMS = #ITEM
UNION ALL
-- Now it finds the parents with hierarchy level for ITEM
-- ie, Black Forest. This is called Recursive query, which works like loop
SELECT I.ID,I.ITEMS,C.CategoryId,C.Category,C.ParentId,[LEVEL] + 1
FROM CTE I
JOIN #Category C ON C.CategoryId=I.ParentId
)
-- Here we keep a column to show header for pivoting ie, CATEGORY0,CATEGORY1 etc
-- and keep these records in a temporary table #NEWTABLE
SELECT ID,ITEMS,CATEGORYID,CATEGORY,PARENTID,
'CATEGORY'+CAST(ROW_NUMBER() OVER(PARTITION BY ITEMS ORDER BY [LEVEL] DESC)-1 AS VARCHAR(4)) COLS,
ROW_NUMBER() OVER(PARTITION BY ITEMS ORDER BY [LEVEL] DESC)-1 [LEVEL]
INTO #NEWTABLE
FROM CTE
ORDER BY ITEMS,[LEVEL]
OPTION(MAXRECURSION 0)
Here is the result from the above query
Explanation
Black Forest comes under Cake.
Cake comes under Bakery.
Bakery comes under Food.
Like this you can create children or parent for any number of levels. Now if you want to add a parent to Food and Beverage, for eg, Food Industry, just add Food Industry to Category table and keep Food Industry's Id as ParentId for Food and Beverage. Thats all.
Now if you want do pivoting, you can follow the below procedures.
1. Get values from column to show those values as column in pivot
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + COLS + ']', '[' + COLS + ']')
FROM (SELECT DISTINCT COLS,[LEVEL] FROM #NEWTABLE) PV
ORDER BY [LEVEL]
2. Now use the below PIVOT query
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT * FROM
(
SELECT ITEMS, CATEGORY, COLS
FROM #NEWTABLE
) x
PIVOT
(
MIN(CATEGORY)
FOR COLS IN (' + #cols + ')
) p
ORDER BY ITEMS;'
EXEC SP_EXECUTESQL #query
Click here to view result
You will get the below result after the pivot
NOTE
If you want all the records irrespective of an item, remove the WHERE clause inside CTE. Click here to view result.
Now I have provided order of columns in pivot table as DESC ie, its shows top-level parent.....Item's parent. If you want to show Item's parent first followed be next level and top-level parent at last, you can change DESC inside the ROW_NUMBER() to ASC. Click here to view result.
According to your schema there's no relationship between 'main category' and 'sub category' but your sample data suggests there would be a relationship, i.e. Alcohol IS A Beverage etc. This sounds like a hierarchy of categories, in which case you could you a single self-referencing Category table instead;
create table dbo.Category (
CategoryID int not null constraint PK_Category primary key clustered (CategoryID),
ParentCategoryID int not null,
CategoryName varchar(100) not null
)
alter table dbo.Category add constraint FK_Category_Category foreign key(ParentCategoryID) references dbo.Category (CategoryID)
insert dbo.Category values (1, 1, 'Beverages')
insert dbo.Category values (2, 1, 'Soft Drink')
insert dbo.Category values (3, 1, 'Alcohol')
This way you can create as many levels of category as you want. Any category where ParentCategoryID = CategoryID is a top level category.
Hope this helps,
Rhys
In order to add a new sub category, you should add the category to the table "ItemSubCategory1" after that you can easily add it to the "Drinks" table.
For Example:
If there is a new category name "Hot Drinks" and a new item "Coffee" which comes in Beverages main category (let CatId=1, MainCatText='Beverages' in ItemMainCategory table) then
INSERT INTO ItemSubCategory1(CatId,SubCatText) VALUES(4,'Hot Drinks')
INSERT INTO Drinks(ItemId,ItemName,ItemMainCategory,ItemSubCategory)
VALUES(5,'Coffee',1,4)