Bringing categories into table rows without using cursors - sql-server

I got a table containing 3 columns:
NameColumn CategoryColumn QuantityColumn
Name1 Category1 5
Name2 Category1 8
Name3 Category1 10
Name4 Category2 3
Name5 Category2 15
Name6 Category2 7
I need to write a query to convert the above data into the following result set:
NameColumn CategoryColumn QuantityColumn
Category1 NULL NULL
Name1 NULL 5
Name2 NULL 8
Name3 NULL 10
Category2 NULL NULL
Name4 NULL 3
Name5 NULL 15
Name6 NULL 7
Is there anyway to do this without using cursors?
Thanks.

SELECT NameColumn, CategoryColumn, QuantityColumn
FROM
(
SELECT CategoryColumn AS NameColumn, NULL AS CategoryColumn, NULL AS QuantityColumn,
CategoryColumn AS _cat, 1 AS _iscat
FROM myTable
GROUP BY CategoryColumn
UNION ALL
SELECT NameColumn, NULL AS CategoryColumn, QuantityColumn,
CategoryColumn AS _cat, 0 AS _iscat
FROM myTable
) x
ORDER BY _cat, _iscat DESC
SQL Fiddle example

You could, but unless you add metadata columns to the output, it will not be of much use.
select NameColumn, CategoryColumn, QuantityColumn, 0 IsCategory
from tbl
union all
select distinct CategoryColumn, CategoryColumn, null, 1
from tbl
order by CategoryColumn, IsCategory desc, NameColumn;
Sure, to get your EXACT output order, you can omit and NULLify certain columns, e.g.
select NameColumn, NULL as CategoryColumn, QuantityColumn
from (
select NameColumn, CategoryColumn, QuantityColumn, 0 IsCategory
from tbl
union all
select distinct CategoryColumn, CategoryColumn, null, 1
from tbl
) X
order by X.CategoryColumn, IsCategory desc, NameColumn;
But if you're intending to use the data outside of SQL Server, you'll want to keep the metadata to relate names and the categories to the original categories they came from. The IsCategory will also be useful to identify the headers that were picked out of the CategoryColumn.

Related

How to filter columns with multiple values for each ID in SQL Server

I have a result set as below and I want to select a single record when the same ID has 2 records with different values for Age and status column, for example
Please see the result set below where ID, name, country name coming from table A and Age, Active status coming from b table
ID name country Age status
----------------------------------------------
1 Prasad India NULL NULL
2 John USA NULL NULL
3 GREG AUS NULL NULL
4 RAVI India NULL NULL
4 RAVI India 18 Years and Above 1
Go with this:
Select *
From
(
Select t2.*,
ROW_NUMBER() over(partition by ID order by name,country,Age, status desc) as rn
From yourtable t2
)
Where rn = 1

GROUP BY on a column to get remove nulls

I have the following table
Order_ID Loc_ID OrderDate ShippingDate DeliveryDate
10 2 10/12/2018 null null
10 2 null null 18/12/2018
10 2 null 12/13/2019 null
Basically, every time a date is recorded, it is added as a row. I want the table to look like this:
Order_ID Loc_ID Order_Date ShippingDate DeliveryDate
10 2 10/12/2018 13/12/2018 18/12/2018
Can someone tell me how I should do this?
Use MAX:
SELECT Order_ID,
Loc_ID,
MAX(OrderDate) AS OrderDate,
MAX(ShippingDate) AS ShippingDate,
MAX(DeliveryDate) AS DeliveryDate
FROM dbo.YourTable
GROUP BY Order_ID,
Loc_ID;
When ordering data NULL has the lowest value, so any non-NULL value will have a "greater" value. As a result MAX will return the non-NULL value.
A simple aggregation should do the trick
Example
Select Order_ID
,Loc_ID
,OrderDate = max(OrderDate)
,ShippingDate = max(ShippingDate)
,DeliveryDate = max(DeliveryDate)
From YourTable
Group By Order_ID,Loc_ID

parent id hierarchy identification MS SqlServer2012

I have this code
create table #temp
(
order_id int not null identity(1,1) primary key
,sid int
,created_date date
,parent_order_id int
)
insert into #temp
(
sid
,created_date
)values(1,'2017-01-01')
insert into #temp
(
sid
,created_date
,parent_order_id
)values(1,'2017-02-01',1),(1,'2017-03-01',2),(1,'2017-04-01',3)
insert into #temp
(
sid
,created_date
)values(1,'2017-06-01')
insert into #temp
(
sid
,created_date
,parent_order_id
)values(1,'2017-07-01',5),(1,'2017-08-01',6)
select * from #temp
Whenever parent_order_id is null which indicates it is a new order. After that customer can add items associated to that order. so we have parent_order_id filled for these associations. But I want to know what is the first order_id for each association child order.I am looking for an output like below.
`order_id sid created_date parent_order_id original_order_id
1 1 2017-01-01 NULL 1
2 1 2017-02-01 1 1
3 1 2017-03-01 2 1
4 1 2017-04-01 3 1
5 1 2017-06-01 NULL 4
6 1 2017-07-01 5 4
7 1 2017-08-01 6 4
`
any help is appreciated. Thanks in advance.
With the following piece of code you can get results you are expecting.
;WITH cte (order_id, original_order_id)
AS
(
SELECT order_id, order_id AS original_order_id
FROM #temp WHERE parent_order_id IS NULL
UNION ALL
SELECT o.order_id AS order_id, cte.original_order_id AS original_order_id
FROM #temp AS o
JOIN cte
ON o.parent_order_id = cte.order_id
)
SELECT #temp.order_id, #temp.sid, #temp.created_date, #temp.parent_order_id, cte.original_order_id
FROM #temp
JOIN cte ON cte.order_id=#temp.order_id
ORDER BY cte.order_id
Please be aware, that there are certain limits on recursion as this for CTE. Currently it is 100 which can be pushed up to 32767.

Set RowNumber by grouping the value if next value is blank

I'm using Sql Server 2008. I want to group the Data by a condition and set the RowNumber for it. None of the columns in the table are unique values.
Grouping Condition
If Any Row occurred as Blank, group the previous not null rows and set Row Number for it.
Example:
My table Data looks like this:
Code Name Location
Db1 Name1 N
Db3 Name3 S
NULL NULL NULL
Db1 Name1 N
NULL NULL NULL
Db1 Name1 N
NULL NULL NULL
Db1 Name1 S
Db4 Name4 S
I need the OUTPUT like this:
Sno Code Name Location
1 Db1 Name1 N
1 Db3 Name3 S
2 Db1 Name1 N
3 Db1 Name1 N
4 Db1 Name1 S
4 Db4 Name4 S
There is no such thing as the next row without having a column to ORDER BY. So I added an id as identity to solve this:
DECLARE #t table(id int identity(1,1), Code char(3), Name char(5), Location char(1))
INSERT #t values
('Db1','Name1','N'),('Db3','Name3','S'),(NULL,NULL,NULL),
('Db1','Name1','N'),(NULL,NULL,NULL),('Db1','Name1','N'),
(NULL,NULL,NULL),('Db1','Name1','S'),('Db4','Name4','S')
;WITH cte as
(
SELECT
row_number() over (order by id)
- row_number() over (order by id * case when code is not null then 1 end) x,
Code, Name, Location
FROM #t
)
SELECT
dense_rank() over (order by x) Sno,
Code, Name, Location
FROM cte
WHERE code is not null
Result:
Sno Code Name Location
1 Db1 Name1 N
1 Db3 Name3 S
2 Db1 Name1 N
3 Db1 Name1 N
4 Db1 Name1 S
4 Db4 Name4 S

sql query to display the input data

I want a SQL query to display the following data
Input Data:
ID GroupID Data
1 1 Hello
2 1 Null
3 1 Null
4 1 World
5 2 Niladri
6 2 XXX
7 2 Null
8 2 PPP
9 2 Null
10 2 Null
11 2 Null
12 2 LLL
as
Output Data:
GroupID MergedData
1 Hello2World
2 NiladriXXX1PPP3LLL
I need to group the data on GroupID and display the result as Hello2World
-->Hello is related to GroupID 1
-->2 is count of NULLS
-->World is related to GroupID 1
Similarly for GroupID 2.
Kindly suggest?
Thanks
As you did not mention your DBMS, this is a solution for PostgreSQL:
SELECT groupid,
string_agg(temp_data,'')
FROM (
SELECT id,
groupid,
data,
CASE
WHEN data IS NULL
THEN cast(max(rn) over (partition by groupid, data) as varchar)
ELSE data
END AS temp_data,
row_number() over (partition by groupid, data) as group_rn
FROM (
SELECT id,
groupid,
data,
CASE
WHEN data IS NULL
THEN row_number() over (partition by groupid,data)
ELSE NULL
END AS rn
FROM foo
) t1
ORDER BY id
) t2
WHERE group_rn in (0,1)
GROUP BY groupid
If your DBMS supports ANSI windowing functions and has something similar like the string_agg() function then this should be portable to your DBMS as well.

Resources