How to retrieve the data on a single row? - sql-server

I have a table with some data, something like this:
+---------+---------+---------+---------+-------------+
| Column1 | Column2 | Column3 | Column4 | Column5 |
+---------+---------+---------+---------+-------------+
| 38073 | 16 | abc | 444 | 4/28/2015 |
| 38076 | 70 | gug | 555 | 4/30/2015 |
| 38098 | 13 | yyy | 111 | 5/12/2015 |
| 38098 | 13 | yyy | 112 | 5/13/2015 |
| 38098 | 13 | yyy | 113 | 5/14/2015 |
| 38098 | 13 | yyy | 114 | 5/15/2015 |
| 38100 | 17 | abc | 115 | 5/13/2015 |
+---------+---------+---------+---------+-------------+
What I want to do is to have the values from Columns 4 and 5 on a single row, something like this :
+---------+----------+-----------+----------+-----------+----------+-----------+----------+-------------+
| Col1 | Col4Val1 | Col5Val1 | Col4Val2 | Col5Val2 | Col4Val3 | Col5Val3 | Col4Val4 | Col5Val4 |
+---------+----------+-----------+----------+-----------+----------+-----------+----------+-------------+
| 38073 | 444 | 4/28/2015 | null | null | null | null | null | null |
| 38076 | 555 | 4/30/2015 | null | null | null | null | null | null |
| 38098 | 111 | 5/12/2015 | 112 | 5/13/2015 | 113 | 5/14/2015 | 114 | 5/15/2015 |
+---------+----------+-----------+----------+-----------+----------+-----------+----------+-------------+
Appreciate the help if possible.
Thank you.
Bogdan

You can use a UNION to unpivot the data with a CTE, then PIVOT the columns. You can achieve this dynamically too, there are hundreds of articles that will show you how to do that:
;WITH CTE AS (
SELECT [Column1], CAST([Column4] AS VARCHAR) AS [ColumnVals], 'Col4Val'+CAST(ROW_NUMBER() OVER(PARTITION BY [Column1] ORDER BY (SELECT 1)) AS VARCHAR) AS [Pivot]
FROM Table1
UNION
SELECT [Column1], [Column5], 'Col5Val'+CAST(ROW_NUMBER() OVER(PARTITION BY [Column1] ORDER BY (SELECT 1)) AS VARCHAR) AS [Pivot]
FROM Table1)
SELECT [Column1], [Col4Val1], [Col5Val1], [Col4Val2], [Col5Val2], [Col4Val3], [Col5Val3], [Col4Val4], [Col5Val4]
FROM CTE
PIVOT (MAX([ColumnVals]) FOR [Pivot] IN ([Col4Val1], [Col5Val1], [Col4Val2], [Col5Val2], [Col4Val3], [Col5Val3], [Col4Val4], [Col5Val4])) PIV
Here's a working fiddle: http://sqlfiddle.com/#!6/e992f/1

Related

How to find max(sortnumber) on item code in SQL Server?

I have following SQL Server table ITEM:
+------------+-----------+------+--------+-----------+------------+
| Date | item_code | name | in/out | total_qty | SortNumber |
+------------+-----------+------+--------+-----------+------------+
| 08/07/2019 | 001 | A | -50 | 100 | 8 |
| 07/07/2019 | 001 | A | 50 | 100 | 7 |
| 06/07/2019 | 003 | C | 25 | 25 | 6 |
| 05/07/2019 | 001 | A | 50 | 50 | 5 |
| 04/07/2019 | 002 | B | 100 | 200 | 4 |
| 03/07/2019 | 003 | C | -25 | 0 | 3 |
| 02/07/2019 | 003 | C | 25 | 25 | 2 |
| 01/07/2019 | 002 | B | 100 | 100 | 1 |
+------------+-----------+------+--------+-----------+------------+
I've tried:
select itemcode, max(Sort_Number)
from ITEM
group by item_code
order by item_code asc
but I want result:
+---------------------+-----------+------------------+
| Distinct(item_code) | Total_qty | Max(Sort_Number) |
+---------------------+-----------+------------------+
| 001 | 100 | 8 |
| 002 | 200 | 4 |
| 003 | 25 | 6 |
+---------------------+-----------+------------------+
Can anyone help me?
The below query gives you the desired result -
With cteItem as
(
select item_code, total_qty, SortNumber,
Row_Number() over (partition by item_code order by SortNumber desc) maxSortNumber
from ITEM
)
select item_code, total_qty, SortNumber from cteItem where maxSortNumber = 1
just need to add max(sort_number) to your query
select item_code ,max(total_qty), max(sort_number)
from ITEM
group by item_code
order by item_code asc

T-SQL: Values are grouped by month, if there is no value for a month the month should also appear and display "NULL"

i have a SQL that displays turnover, stock and other values for stores grouped by month. Logically, if there is no value for a month, the month doesn't appear. The target is that the empty month should appear and display "NULL" for the values. The empty months should range from the #FROM to the #TO parameter (201807 to 201907) in this case.
Before:
+-------+--------+----------+----------+-------+
| Store | Month | Incoming | Turnover | Stock |
+-------+--------+----------+----------+-------+
| 123 | 201810 | 5 | 4 | 1 |
| 123 | 201811 | 0 | 1 | 0 |
| 123 | 201901 | 25 | 5 | 20 |
| 123 | 201902 | 5 | 10 | 15 |
| 123 | 201903 | 8 | 9 | 14 |
| 123 | 201904 | 5 | 4 | 15 |
| 123 | 201905 | 10 | 5 | 20 |
+-------+--------+----------+----------+-------+
After:
+-------+--------+----------+----------+-------+
| Store | Month | Incoming | Turnover | Stock |
+-------+--------+----------+----------+-------+
| 123 | 201807 | NULL | NULL | NULL |
| 123 | 201808 | NULL | NULL | NULL |
| 123 | 201809 | NULL | NULL | NULL |
| 123 | 201810 | 5 | 4 | 1 |
| 123 | 201811 | 0 | 1 | 0 |
| 123 | 201812 | NULL | NULL | NULL |
| 123 | 201901 | 25 | 5 | 20 |
| 123 | 201902 | 5 | 10 | 15 |
| 123 | 201903 | 8 | 9 | 14 |
| 123 | 201904 | 5 | 4 | 15 |
| 123 | 201905 | 10 | 5 | 20 |
| 123 | 201906 | NULL | NULL | NULL |
| 123 | 201907 | NULL | NULL | NULL |
+-------+--------+----------+----------+-------+
Code Example: db<>fiddle
I have absolutely no idea how to solve this and will thank you in advance for your help! :)
You can try to use cte recursive make a calendar table, then do outer-join
;WITH CTE AS (
SELECT CAST(CAST(#FROM AS VARCHAR(10)) + '01' AS DATE) fromDt,
CAST(CAST(#TO AS VARCHAR(10)) + '01' AS DATE) toDt,
Store
FROM (SELECT DISTINCT Store FROM #Test) t1
UNION ALL
SELECT DATEADD(MONTH,1,fromDt),toDt,Store
FROM CTE
WHERE DATEADD(MONTH,1,fromDt) <= toDt
)
SELECT FORMAT(fromDt,'yyyyMM') Month,
c.Store,
t.Incoming,
t.Turnover,
t.Stock
FROM CTE c
LEFT JOIN #Test t on
c.fromDt = CAST(CAST(t.Month AS VARCHAR(10)) + '01' AS DATE)
and
c.Store = t.Store
sqlfiddle

Cumulative Count of NULL restarting at NOT NULL

I would like to add a column indicating the number invites a person received before they accepted by incrementally counting the number of null columns before a non-null while partitioning over the PERSON_ID and ordering by the INVITED_DATE.
My table has the following format:
| UNIQUE_ID | PERSON_ID | INVITED_DATE | ACCEPTED_DATE |
| 12345 | 567 | 12-01-18 | NULL |
| 12346 | 567 | 12-02-18 | NULL |
| 12347 | 567 | 12-03-18 | NULL |
| 12348 | 567 | 12-04-18 | 12-04-18 |
| 12349 | 567 | 12-05-18 | NULL |
| 12350 | 568 | 12-01-18 | NULL |
| 12351 | 568 | 12-02-18 | 12-02-18 |
The output should ideally look like the following:
| UNIQUE_ID | PERSON_ID | INVITED_DATE | ACCEPTED_DATE | INVITES_BEFORE_ACCEPT |
| 12345 | 567 | 12-01-18 | NULL | 1 |
| 12346 | 567 | 12-02-18 | NULL | 2 |
| 12347 | 567 | 12-03-18 | NULL | 3 |
| 12348 | 567 | 12-04-18 | 12-04-18 | 0 |
| 12349 | 567 | 12-05-18 | NULL | 1 |
| 12350 | 568 | 12-01-18 | NULL | 1 |
| 12351 | 568 | 12-02-18 | 12-02-18 | 0 |
So far I've tried a number iterations of ROW NUMBER with OVER and PARTITION but I've found it will need to be an OUTER APPLY. The following OUTER APPLY counts over the data but doesn't restart the count with a successful accept.
SELECT t.* , invites.INVITES_BEFORE_ACCEPT
FROM table t
OUTER APPLY (
SELECT COUNT(*) INVITES_BEFORE_ACCEPT
FROM table t2
WHERE t.PERSON_ID = t2.PERSON_ID and t.INVITED_DATE < t2.ACCEPTED_DATE
) invites
One way would be
WITH t
AS (SELECT *,
COUNT(ACCEPTED_DATE)
OVER (
PARTITION BY PERSON_ID
ORDER BY INVITED_DATE) AS Grp
FROM [table])
SELECT *,
SUM(CASE
WHEN ACCEPTED_DATE IS NULL
THEN 1
ELSE 0
END)
OVER (
PARTITION BY PERSON_ID, Grp
ORDER BY INVITED_DATE) AS INVITES_BEFORE_ACCEPT
FROM t
Demo

SQL Server : pivot a single column with fixed number or columns

I have this simple query that brings the siblings of a given item.
select
PC.SKU
from
ProdC PC
where
Parent_ID in (select Parent_ID
from ProdC
where SKU = 4536)
and ParentFlag <> 'P'
and SKU <> 4536
I'd like to display up to 6 siblings horizontally. So it would look something like this:
Sib1 Sib2 Sib3 Sib4 Sib5 Sib6
=============================================
4532 4539 4548 4552 4561 4562
3512 3536
5632 5636 5640
Now each of these row are for a different item. some parent have 2 child, some have up to 8 but I only want to show 6 max. There's a priority column for the children. I can sort it by that column desc to get the 6 newest child.
Any help appreciated.
I have a table in my test database with records with multiple child records for each parent record, created the following query to only get the top 6 child records, but it only picks the immediate child records, if you have another level of children records then you probably need to look into recursive CTE etc.
;WITH X AS
(
Select *
,ROW_NUMBER() OVER (PARTITION BY Parent_ID ORDER BY ID) rn
from TableName
),
Y AS (
Select * , 'Sib' + Cast(rn AS Varchar(10)) Sibs
FROM X
Where rn < 7
)
Select *
from
(
Select Parent_ID , Sibs , ID
FROM Y
) a
PIVOT (MAX(ID)
FOR Sibs
IN (Sib1,Sib2,Sib3,Sib4,Sib5,Sib6))p
Result Set
+----------+------+------+------+------+-------+------+
| ParentID | Sib1 | Sib2 | Sib3 | Sib4 | Sib5 | Sib6 |
+----------+------+------+------+------+-------+------+
| 0 | 0 | 139 | 258 | 266 | 285 | 500 |
| 139 | 140 | 141 | 142 | 143 | 144 | 162 |
| 142 | 5062 | 5063 | NULL | NULL | NULL | NULL |
| 143 | 5041 | 5042 | 5043 | 5044 | 5045 | 5046 |
| 144 | 5050 | 5051 | 5052 | 5053 | 5054 | 5055 |
| 258 | 5823 | 5824 | 5825 | 5826 | 11269 | NULL |
| 266 | 5822 | 5912 | 5913 | 5914 | 5915 | 5916 |
| 285 | 2139 | 3855 | 4172 | 4173 | NULL | NULL |
+----------+------+------+------+------+-------+------+
EDIT
After you have provided some sample data your query should look something like..
;WITH X AS
(
Select *
,ROW_NUMBER() OVER (PARTITION BY Parent_Sku ORDER BY sku) rn
from #ProdC
),
Y AS (
Select * , 'Sib' + ISNULL(NULLIF(Cast(rn -1 AS Varchar(10)), '0'),'') Sibs
FROM X
Where rn < 8
)
Select Sib1,Sib2,Sib3,Sib4,Sib5,Sib6
from
(
Select Parent_Sku , Sibs , sku
FROM Y
) a
PIVOT (MAX(sku)
FOR Sibs
IN (Sib,Sib1,Sib2,Sib3,Sib4,Sib5,Sib6,Sib7))p
Result set
| Sib1 | Sib2 | Sib3 | Sib4 | Sib5 | Sib6 |
|------|------|------|--------|--------|--------|
| 4532 | 4536 | 4539 | 4548 | 4552 | (null) |
| 3512 | 3536 | 4561 | 4562 | (null) | (null) |
| 5632 | 5636 | 5640 | (null) | (null) | (null) |

Reformat existing table from paired columns into rows

For example I have a table with 5 rows and 7 columns, I wish to move the last two columns into the previous two columns. New format of table would now be 10 rows and 5 columns
Present Table format
+-----+------------+----------+------------+---------------+------------+---------------+
| id | VisitDate | fkFamily | child1.DOB | child1.Gender | child2.DOB | child2.Gender |
+-----+------------+----------+------------+---------------+------------+---------------+
| 78 | 19/04/2010 | 277 | 14/03/2009 | 0 | NULL | NULL |
| 79 | 20/04/2010 | 289 | 12/08/2007 | 0 | NULL | NULL |
| 107 | 20/04/2010 | 191 | NULL | NULL | NULL | NULL |
| 108 | 20/04/2010 | 259 | NULL | NULL | 31/03/2010 | 1 |
| 109 | 20/04/2010 | 126 | NULL | NULL | NULL | NULL |
+-----+------------+----------+------------+---------------+------------+---------------+
New table format
+-----+------------+----------+------------+----------------------+
| id | VisitDate | fkFamily | child.DOB | child.Gender |
+-----+------------+----------+------------+----------------------+
| 78 | 19/04/2010 | 277 | 14/03/2009 | 0 |
| 79 | 20/04/2010 | 289 | 12/08/2007 | 0 |
| 107 | 20/04/2010 | 191 | NULL | NULL |
| 108 | 20/04/2010 | 259 | NULL | NULL |
| 109 | 20/04/2010 | 126 | NULL | NULL |
| 78 | 19/04/2010 | 277 | NULL | NULL |
| 79 | 20/04/2010 | 289 | NULL | NULL |
| 107 | 20/04/2010 | 191 | NULL | NULL |
| 108 | 20/04/2010 | 259 | 31/03/2010 | 1 |
| 109 | 20/04/2010 | 126 | NULL | NULL |
+-----+------------+----------+------------+----------------------+
You can get the final result by unpivoting the columns Child1_DOB, Child1_Gender, etc. Starting in SQL Server 2005, the unpivot function was made available but for your case I'd actually use CROSS APPLY so you can unpivot the Child1, and Child2 values in pairs.
The syntax would be:
select
t.id,
t.visitdate,
t.fkFamily,
c.child_DOB,
c.child_Gender
from yourtable t
cross apply
(
select child1_DOB, child1_Gender union all
select child2_DOB, child2_Gender
) c (child_DOB, child_Gender);
See SQL Fiddle with Demo
Then you could also include an identifier for each of the values so you know if it belonged to child one or two:
select
t.id,
t.visitdate,
t.fkFamily,
c.child,
c.child_DOB,
c.child_Gender
from yourtable t
cross apply
(
select 'Child1', child1_DOB, child1_Gender union all
select 'Child2', child2_DOB, child2_Gender
) c (child, child_DOB, child_Gender)
See SQL Fiddle with Demo. These give a result similar to:
| ID | VISITDATE | FKFAMILY | CHILD_DOB | CHILD_GENDER |
|-----|------------|----------|------------|--------------|
| 78 | 19/04/2010 | 277 | 14/03/2009 | 0 |
| 78 | 19/04/2010 | 277 | (null) | (null) |
| 79 | 20/04/2010 | 289 | 12/08/2007 | 0 |
| 79 | 20/04/2010 | 289 | (null) | (null) |
| 107 | 20/04/2010 | 191 | (null) | (null) |
| 107 | 20/04/2010 | 191 | (null) | (null) |
| 108 | 20/04/2010 | 259 | (null) | (null) |
| 108 | 20/04/2010 | 259 | 31/03/2010 | 1 |
| 109 | 20/04/2010 | 126 | (null) | (null) |
| 109 | 20/04/2010 | 126 | (null) | (null) |
You could reformat the table into something like this by using UNION:-
SELECT * FROM (
SELECT id, VisitDate, fkFamily, child1_DOB as child_DOB, child1_Gender as child_Gender
FROM yourtable
UNION
SELECT id, VisitDate, fkFamily, child2_DOB, child2_Gender
FROM yourtable) as temp
FIDDLE
You could use SELECT INTO if you wanted to create a new table from the results, for example:-
SELECT * INTO yournewtable FROM (
SELECT id, VisitDate, fkFamily, child1_DOB as child_DOB, child1_Gender as child_Gender
FROM yourtable
UNION
SELECT id, VisitDate, fkFamily, child2_DOB, child2_Gender
FROM yourtable) as temp

Resources