Unpivot or Something else - sql

I have a table with 2 columns that should only contain one value but some entries contains 2 or 3 values . all the other columns are the same for these problem rows .
Table A - Currently
Deal ID | PA ID | other columns
1 2 xxxxx
1,2 2 xxxxx
3 1,5 xxxxx
What I want
Deal ID | PA ID | other columns
1 2 xxxxx
1 2 xxxxx
2 2 xxxxx
3 1 xxxxx
3 5 xxxxx
Not sure how to do this ? Think I need to UNPIVOT and then remove the ,.

Here is one solution. It is brute force and uses union all to bring get multiple copies:
with incols as (
select (case when charindex(Dealid, ',') > 0
then left(DealId, charindex(Dealid, ',') - 1)
else DealId
end) as DealId1,
(case when charindex(Dealid, ',') > 0
then substring(DealId, charindex(DealId, ',') + 1, 100)
end) as DealId2,
(case when charindex(PAId, ',') > 0
then left(PAId, charindex(PAId, ',') - 1)
else PAId
end) as PAId1,
(case when charindex(PAId, ',') > 0
then substring(PAId, charindex(PAId, ',') + 1, 100)
end) as PAId2,
t.*
from t
),
deals as (
select (case when whichdeal = 1 then deal1 else deal2 end) as newdeal, t.*
from ((select *, 1 as whichdeal
from t
) union all
(select *, 2 as whichdeal
from t
where deal2 is not null
)) t
)
select newdeal as dealid, t.*
from deals
Including PAs requires adding another CTE, and then joining deals and PAs on dealid and PA id to get all possible combinations. You didn't specify exactly what you want to happen when there are duplicates in both rows, so I'm just guessing that you would want all combinations.

The solution was :
DECLARE #t TABLE (
DealID VARCHAR(10),
PAID VARCHAR(200),
[DESC] VARCHAR(100))
INSERT #t
SELECT '1',
'2',
'xxxx'
UNION ALL
SELECT '1,2',
'2',
'xxxx'
UNION ALL
SELECT '3',
'1,5',
'xxxx'
SELECT LEFT(b, Charindex(',', b + ',') - 1) AS DealID,
LEFT(d, Charindex(',', d + ',') - 1) AS PAID,
[Desc]
FROM (SELECT Substring(DealID, DE.number, 200) AS b,
Substring(PAID, PA.number, 200) AS d,
[Desc]
FROM #t DealID
LEFT JOIN (SELECT DISTINCT number
FROM master.dbo.spt_values
WHERE number BETWEEN 1 AND 200) PA
ON Substring(',' + PAID, PA.number, 1) = ','
LEFT JOIN (SELECT DISTINCT number
FROM master.dbo.spt_values S
WHERE number BETWEEN 1 AND 200) DE
ON Substring(',' + DealID, DE.number, 1) = ',') t

Related

How to combine each three rows into single column in SQL Server 2008?

NOOfDays DISTRInutorID
-------------------------
1 abcd
1 cdef
2 DFSDF
2 SFSDD
2 SDFSD
2 WAOYWAR
7 WEFIWE
7 WEOFYWE
7 WFYREU
The above is one of my sample tables, I want to combine each two rows based on NOOfDays.
Expected output:
NOOfDays DiSTRInutorID
------------------------------
1 abcd, cdef
2 DFSDF, SFSDD
2 SDFSD, WAOYWAR
7 WEFIWE, WEOFYWE
7 WFYREU
In ancient versions of SQL Server, the logic looks like:
select n.NOOfDays,
stuff( (select ',' + t2.DISTRInutorID
from t t2
where t2.NOOfDays = n.NOOfDays
for xml path ('')
), 1, 1, ''
) as list
from (select distinct NOOfDays from t) n;
EDIT:
I misunderstood the original question. This is what you are looking for:
with cte as (
select t.*, row_number() over (order by (select null)) - 1 as seqnum
from t
)
select NOOfDays,
(case when count(*) = 1 then min(DISTRInutorID)
else max(case when seqnum % 2 = 0 then DISTRInutorID end) + ',' + max(case when seqnum % 2 = 1 then DISTRInutorID end)
end) as list
from cte
group by NOOfDays, floor(seqnum / 2);
Note that SQL tables represent unordered sets. This arbitrarily pairs rows where the first column is the same, but there is no guarantee that these are adjacent -- for the simple reason that "adjacent" is not defined.
Here is a db<>fiddle.

count rows which have max value less than specified parameter

I want to find in my table, max value which is less than specified in parameter and get count of rows that have the same value as max value. For example in my table I have values: (4,1,3,1,4,4,10), and it is list of parameters in string "2,9,10,4". I have to split string to separate parameters. Base on this sample values I want to get something like that:
param | max value | count
2 | 1 | 2
9 | 4 | 3
10 | 4 | 3
4 | 3 | 1
And it is my sample query:
select
[param]
, max([val]) [max_value_by_param]
, max(count) [count]
from(
select
n.value as [param]
,a.val
, count(*) as [count]
from (--mock of table
select 1 as val union all
select 3 as val union all
select 4 as val union all
select 1 as val union all
select 3 as val union all
select 4 as val union all
select 4 as val union all
select 10 as val
) a
join (select [value] from string_split('2,9,10,4', ',')) n--list of params
on a.val < n.[value]
group by n.value, a.val
) tmp
group by [param]
Is it possible to do it better/easier ?
Here is a way to express this using apply:
select s.value as param, a.val, a.cnt
from string_split('2,9,10,4', ',') s outer apply
(select top (1) a.val, count(*) as cnt
from a
group by a.val
having a.val < s.value
order by a.val desc
) a;
Here is a db<>fiddle.
But the fastest method is probably going to be:
with av as (
select a.val, count(*) as cnt
from a
group by a.val
union all
select s.value, null as cnt
from string_split('2,9,10,4', ',') s
)
select val, a_val, a_cnt
from (select av.*,
max(case when cnt is not null then val end) over (order by val, (case when cnt is null then 1 else 2 end)) as a_val,
max(case when cnt is not null then cnt end) over (order by val, (case when cnt is null then 1 else 2 end)) as a_cnt
from av
) av
where cnt is null;
This only aggregates the data once and should return all parameters, even those with no preceding values in a.

Select non existing Numbers from Table each ID

I‘m new in learning TSQL and I‘m struggling getting the numbers that doesn‘t exist in my table each ID.
Example:
CustomerID Group
1 1
3 1
6 1
4 2
7 2
I wanna get the ID which does not exist and select them like this
CustomerID Group
2 1
4 1
5 1
5 2
6 2
....
..
The solution by usin a cte doesn‘t work well or inserting first the data and do a not exist where clause.
Any Ideas?
If you can live with ranges rather than a list with each one, then an efficient method uses lead():
select group_id, (customer_id + 1) as first_missing_customer_id,
(next_ci - 1) as last_missing_customer_id
from (select t.*,
lead(customer_id) over (partition by group_id order by customer_id) as next_ci
from t
) t
where next_ci <> customer_id + 1
Cross join 2 recursive CTEs to get all the possible combinations of [CustomerID] and [Group] and then LEFT join to the table:
declare #c int = (select max([CustomerID]) from tablename);
declare #g int = (select max([Group]) from tablename);
with
customers as (
select 1 as cust
union all
select cust + 1
from customers where cust < #c
),
groups as (
select 1 as gr
union all
select gr + 1
from groups where gr < #g
),
cte as (
select *
from customers cross join groups
)
select c.cust as [CustomerID], c.gr as [Group]
from cte c left join tablename t
on t.[CustomerID] = c.cust and t.[Group] = c.gr
where t.[CustomerID] is null
and c.cust > (select min([CustomerID]) from tablename where [Group] = c.gr)
and c.cust < (select max([CustomerID]) from tablename where [Group] = c.gr)
See the demo.
Results:
> CustomerID | Group
> ---------: | ----:
> 2 | 1
> 4 | 1
> 5 | 1
> 5 | 2
> 6 | 2

Filter duplicates on cross fields

So, i have some records:
[ fid sid ]
1 2
1 3
1 4
2 1
2 3
2 4
3 1
3 2
3 4
....
Both fields contains ids. I need to get only uniq or first not-uniq records, but uniq by cross fields.
For example [2,1] and [1,2] are not uniq.
In the end i want to have:
[ fid sid ]
1 2
1 3
1 4
2 3
2 4
3 4
....
Those are records, that have been filtered:
[ fid sid ]
2 1
3 1
3 2
....
Thanks for the answers!
If you have no duplicates, you can do:
select fid, sid
from t
where fid <= sid
union all
select fid, sid
from t
where fid > sid and
not exists (select 1 from t t2 where t2.fid = t.sid and t2.sid = t.fid);
If you do have duplicates and don't care about the ordering, you can do:
select (case when fid < sid then fid else sid end) as sid,
(case when fid < sid then sid else fid end) as sid
from t
group by (case when fid < sid then fid else sid end),
(case when fid < sid then sid else fid end);
This could produce pairs that are not in the original data (because the inverse is in the data).
Here's a way that uses a left self-join that only keeps those without a match.
Example code:
declare #T table (fid int, [sid] int);
insert into #T (fid, [sid]) values
(1, 2),(1, 3),(1, 4),
(2, 1),(2, 3),(2, 4),
(3, 1),(3, 2),(3, 4);
select distinct t.fid, t.[sid]
from #T t
left join #T t2 on (t2.[sid] = t.fid and t2.fid = t.[sid] and t2.fid < t2.[sid])
where t2.fid is null
order by t.fid, t.[sid];
Result:
fid sid
1 2
1 3
1 4
2 3
2 4
3 4
Same result with a NOT EXISTS:
select distinct fid, [sid]
from #T t
where not exists (
select 1 from #T t2
where t2.[sid] = t.fid and t2.fid = t.[sid] and t2.fid < t2.[sid]
)
order by fid, [sid];
Another aproach where ordering is available would be:
SELECT DISTINCT [fid] ,[sid]
FROM aaa where
CAST(fid AS VARCHAR(2) ) + '.'+ CAST(sid AS VARCHAR(2) ) not in
(SELECT DISTINCT CAST(sid AS VARCHAR(2) ) + '.'+ CAST(fid AS VARCHAR(2))
FROM aaa)
Although this might not be recommended based on how many times you execute is and how much data will be processed.

duplicate entry in union

I have three tables:
1. Flat Discount
2. Promotion
3. weeklyorder
When i join these table and take union i got 2 row with same data but one different .. how to merge it to show only one row.
Query:
SELECT skuMaster.SKU,
(skuMaster.MinimumStock - COUNT(*)) as ReorderQuantity,
'LowInventory' as descp
FROM SKUMaster skuMaster
JOIN InventoryMaster inventoryMaster ON skuMaster.SKU = inventoryMaster.SKU
GROUP BY skuMaster.sku, skuMaster.MinimumStock, skuMaster.Name
HAVING COUNT(*) < skuMaster.MinimumStock
UNION
SELECT WeeklyOrderList.SKU,
WeeklyOrderList.Quantity as ReorderQuantity,
'NoPO' as descp
FROM WeeklyOrderList
WHERE WeeklyOrderList.POCGen = 'true'
result :
SKU ReorderQuantity descp
1 1 LowInventory
2 2 LowInventory
2 2 NoPO
6 5 LowInventory
here 2nd And 3rd are alomost same only description is different.
can we combine them and show only one row with descp as lowinventory and NOPO
SKU ReorderQuantity descp
1 1 LowInventory
2 2 LowInventory NoPo
6 5 LowInventory
same as above suppose we have table below
SKU ReorderQuantity
1 1
2 5
2 10
6 5
here output should be Max reorder quantity of same sku
Result:
SKU ReorderQuantity
1 1
2 10
6 5
;WITH CTE AS
(
SELECT skuMaster.SKU,
(skuMaster.MinimumStock - COUNT(*)) as ReorderQuantity,
'LowInventory' as descp
FROM SKUMaster skuMaster
JOIN InventoryMaster inventoryMaster ON skuMaster.SKU = inventoryMaster.SKU
GROUP BY skuMaster.sku, skuMaster.MinimumStock, skuMaster.Name
HAVING COUNT(*) < skuMaster.MinimumStock
UNION
SELECT WeeklyOrderList.SKU,
WeeklyOrderList.Quantity as ReorderQuantity,
'NoPO' as descp
FROM WeeklyOrderList
WHERE WeeklyOrderList.POCGen = 'true'
)
SELECT DISTINCT
a.SKU,
a.ReorderQuantity,
descp = STUFF((SELECT ', ' + b.descp
FROM CTE b
WHERE b.ReorderQuantity = a.ReorderQuantity
FOR XML PATH('')), 1, 2, '')
FROM CTE a
just an example for above output mentioned
DECLARE #t TABLE
(
SKU INT,
ReorderQuantity INT
)
INSERT INTO #t (SKU,ReorderQuantity)
VALUES (1,1), (2,5), (2,10), (6,5)
SELECT t.SKU,tt.Qty
FROM #t t
INNER JOIN (SELECT MAX(ReorderQuantity)as Qty, SKU
FROM #t
GROUP BY SKU) tt
ON tt.SKU = t.SKU
GROUP BY t.SKU,tt.Qty