TSQL UNION GETTING UNIQUE VALUE - sql-server

I'm trying to get the unique record between two databases based on two criteria. The criteria is:
If the data is found in database 1 (#SCCM in my example below), it
is given preference
Grab the MAX resource id within the selected database
Here is an example, which is half working. The database preference is working, but the maximum resource id WITHIN that database isn't. Right now it's selecting the max between both #SMS and #SCCM
DECLARE #SMS TABLE (
name0 varchar(100),
resid int
)
DECLARE #SCCM TABLE (
name0 varchar(100),
resid int
)
INSERT INTO #SMS
SELECT 'TEST', 1000 UNION
SELECT 'TEST', 1500 UNION
SELECT 'TEST1', 2000 UNION
SELECT 'TEST2', 3000 UNION
SELECT 'TEST3', 4000
INSERT INTO #SCCM
SELECT 'TEST', 100 UNION
SELECT 'TEST', 150 UNION
SELECT 'TEST1', 200 UNION
SELECT 'TEST2', 300
SELECT MIN(SMSDB) as SMSDB, MAX(Resid), Name0 FROM
(
SELECT name0, resid, 2 as SMSDB FROM #SMS
UNION ALL
SELECT name0, resid, 1 as SMSDB FROM #SCCM
) as tbl
GROUP BY NAME0
Expected results:
SMSDB | Resid | Name0
----------------------
1 | 150 | TEST
1 | 200 | TEST1
1 | 300 | TEST2
2 | 4000 | TEST3

You can use partitions:
;WITH tbl as
(
SELECT name0, resid, 2 as SMSDB FROM SMS
UNION ALL
SELECT name0, resid, 1 as SMSDB FROM SCCM
),
t as (
SELECT *,
ROW_NUMBER()
over (partition By name0 order by SMSDB, resid desc )
as rn
FROM tbl
)
SELECT * FROM t
WHERE rn = 1
Results:
| NAME0 | RESID | SMSDB | RN |
------------------------------
| TEST | 150 | 1 | 1 |
| TEST1 | 200 | 1 | 1 |
| TEST2 | 300 | 1 | 1 |
| TEST3 | 4000 | 2 | 1

The partition solution may in fact be better, but it hurts my brain. What about just excluding the values in SMS if they exist in SCCM?
SELECT MIN(SMSDB) as SMSDB, MAX(Resid), Name0 FROM
(
SELECT name0, resid, 2 as SMSDB FROM #SMS SMS
WHERE NOT EXISTS (SELECT * FROM #SCCM WHERE name0 = SMS.name0)
UNION ALL
SELECT name0, resid, 1 as SMSDB FROM #SCCM
) as tbl
GROUP BY NAME0
or even
SELECT 1 as SMSDB, MAX(resid), name0 FROM #SCCM
GROUP BY name0
UNION ALL
SELECT 2 as SMSDB, MAX(resid), name0 FROM #SMS SMS
WHERE NOT EXISTS (SELECT * FROM #SCCM WHERE name0 = SMS.name0)
GROUP BY name0
ORDER BY name0

Related

Displaying data in a different way

I have a log table that looks like this:
ProductId | OldDescription | NewDescription | OldTagId | NewTagId |
-------------------------------------------------------------------
12345 | description1 | description2 | 1 | 5 |
and I want to display it this way:
ProductId | ChangeId | OldVal | NewVal |
----------------------------------------------------------
12345 | 1 | description1 | description2 |
12345 | 2 | 1 | 5 |
Where the data in the ChangeId corresponds to the type of the value changed (Description, TagId)
How could I approach this?
Thank you
Just another option via CROSS APPLY
Example
Declare #YourTable Table ([ProductId] varchar(50),[OldDescription] varchar(50),[NewDescription] varchar(50),[OldTagId] int,[NewTagId] int)
Insert Into #YourTable Values
(12345,'description1','description2',1,5)
Select ProductID
,B.*
From #YourTable A
Cross Apply ( values (1,[OldDescription],[NewDescription])
,(2,left([OldTagId],25),left([NewTagId],25))
) B(ChangeID,OldVal,NewVal)
Returns
ProductID ChangeID OldVal NewVal
12345 1 description1 description2
12345 2 1 5
Just for fun:
I saw the comment of 30 columns. If performance is NOT essential, here is option that will dynamically pivot your data without actually using dynamic SQL
Select *
From (
Select ProductID
,C.*
From #YourTable A
Cross Apply ( values (cast((Select A.* for XML RAW) as xml))) B(XMLData)
Cross Apply (
Select Item = left(xAttr.value('local-name(.)', 'varchar(100)'),3)+'Val'
,Value = xAttr.value('.','varchar(100)')
,ChangeID = ((row_number() over (order by (select null)) - 1 ) / 2)+1
From XMLData.nodes('//#*') xNode(xAttr)
Where xAttr.value('local-name(.)','varchar(100)') not in ('ProductID','Other','ColumnsToExclude')
) C
) src
Pivot ( max(Value) for Item in ([OldVal],[NewVal]) ) pvt
See if something like that works for you:
SELECT ProductId,
1 AS ChangeId,
OldDescription AS OldVal,
NewDescription AS NewVal
FROM log
UNION
SELECT ProductId,
2 AS ChangeId,
OldTagId AS OldVal,
NewTagId AS NewVal
FROM log
ORDER BY ProductId,
ChangeId

Split String Into Individual Rows

I'm currently tying to figure out how to split an ID into 2 rows at each instance of '/'. The original IDs will still be saved in the main table as well as temp table 2 but I need the new IDs saved to a new table. All of this happens in temporary tables on a pre-import handler before a report is generated.
The tables output is currently as follows:
RWID RWLEN DESCR QTY UNIT
T2/10060 20.0000 SomeInfo 1 pcs
T2/10061 18.5689 SomeInfo 1 pcs
T2/10062 20.0000 SomeInfo 1 pcs
I need the table to out the following:
RWID RWLEN DESCR QTY UNIT
T10060 20.0000 SomeInfo 1 pcs
T20060 20.0000 SomeInfo 1 pcs
T10061 18.5689 SomeInfo 1 pcs
T20061 18.5689 SomeInfo 1 pcs
T10062 20.0000 SomeInfo 1 pcs
T20062 20.0000 SomeInfo 1 pcs
A snippet of my code is below:
-- populate temp table 1 from main table
SELECT *
INTO ##tmp1
FROM main;
-- populate temp table 2 from temp table 1, group and order by RWID
SELECT RWID, MAX(DESCR) as aux
INTO ##tmp2
FROM ##tmp1
group by RWID
ORDER by RWID;
-- populate temp table 3 from temp table 1 then split strings with dividers
SELECT RWID, RWLEN, DESCR, QTY, UNIT
INTO ##tmp3
FROM ##tmp1
UNION ALL
SELECT RWID, NULL RWLEN, NULL DESCR, NULL QTY, NULL UNIT
FROM ##tmp1
GROUP BY RWID
ORDER BY RWID, DESCR desc;
SELECT
RWID = CASE WHEN a.DESCR = b.AUX THEN a.RWID ELSE NULL END,
RWLEN = CASE WHEN a.DESCR = b.AUX THEN a.RWLEN ELSE NULL END,
a.DESCR,
a.QTY,
a.UNIT
INTO ##report
FROM ##tmp3
a
FULL OUTER JOIN ##tmp2
b on a.RWID = b.RWID;
SELECT *
FROM ##report
Thanks in advance for your time and assistance.
UPDATE! Thanks so much for all of your help, it really steered me in the right direction. I've figured out how to split the strings shown above as well as the other types of IDs that I'll encounter that I hadn't included in the example. Thanks again for your time and help, you're all awesome!!
Result: http://www.sqlfiddle.com/#!18/17a09/1
You can simply use a UNION ALL like following to get the desired output.
SELECT 'T1'+ SUBSTRING(RWID,CHARINDEX('/',RWID)+1,
LEN(RWID)- CHARINDEX('/',RWID)) -- + OTHER COLUMN
FROM [TABLE_NAME]
UNION ALL
SELECT 'T2'+ SUBSTRING(RWID,CHARINDEX('/',RWID)+1,
LEN(RWID)- CHARINDEX('/',RWID)) -- + OTHER COLUMN
FROM [TABLE_NAME]
Complete Example
DECLARE #TBL TABLE (RWID VARCHAR(30), RWLEN DECIMAL(15,2),
DESCR VARCHAR(50), QTY INT, UNIT VARCHAR(4))
INSERT INTO #TBL
values
('T2/10060', 20.0000 ,'SomeInfo', 1 ,'pcs'),
('T2/10061', 18.5689 ,'SomeInfo', 1 ,'pcs'),
('T2/10062', 20.0000 ,'SomeInfo', 1 ,'pcs')
SELECT 'T1'+ SUBSTRING(RWID, CHARINDEX('/',RWID)+1,LEN(RWID)- CHARINDEX('/',RWID)) RWID
,RWLEN, DESCR, QTY, UNIT
FROM #TBL
UNION ALL
SELECT 'T2'+ SUBSTRING(RWID, CHARINDEX('/',RWID)+1,LEN(RWID)- CHARINDEX('/',RWID)) RWID
,RWLEN, DESCR, QTY, UNIT
FROM #TBL
Output
+---------+-------+----------+-----+------+
| RWID | RWLEN | DESCR | QTY | UNIT |
+---------+-------+----------+-----+------+
| T110060 | 20.00 | SomeInfo | 1 | pcs |
+---------+-------+----------+-----+------+
| T110061 | 18.57 | SomeInfo | 1 | pcs |
+---------+-------+----------+-----+------+
| T110062 | 20.00 | SomeInfo | 1 | pcs |
+---------+-------+----------+-----+------+
| T210060 | 20.00 | SomeInfo | 1 | pcs |
+---------+-------+----------+-----+------+
| T210061 | 18.57 | SomeInfo | 1 | pcs |
+---------+-------+----------+-----+------+
| T210062 | 20.00 | SomeInfo | 1 | pcs |
+---------+-------+----------+-----+------+
DEMO
SQL-server using CTE
declare #table table (rwid varchar(30), rwlen float, descr varchar(50), qty int, unit varchar(4))
insert into #table
values
('T2/10060', 20.0000 ,'SomeInfo', 1 ,'pcs'),
('T2/10061', 18.5689 ,'SomeInfo', 1 ,'pcs'),
('T2/10062', 20.0000 ,'SomeInfo', 1 ,'pcs')
;with mycte as (
select *, cast(right(left(rwid,charindex('/',rwid)-1),1) as int) [num], 1 [start] from #table
union all
select t.*,c.start + 1, c.num from #table t
inner join mycte c
on c.rwid = t.rwid
and c.start + 1 <= c.num
)
select
concat(left(rwid,1), start,replace(rwid,left(rwid,charindex('/',rwid)+1),'')) ,
rwlen,
descr,
qty,
unit
from mycte
order by rwid, start
Using Cross apply
;WITH CTE( RWID, RWLEN,DESCR,QTY, UNIT)
AS
(
SELECT 'T2/10060',20.0000,'SomeInfo', 1,'pcs' UNION ALL
SELECT 'T2/10061',18.5689,'SomeInfo', 1,'pcs' UNION ALL
SELECT 'T2/10062',20.0000,'SomeInfo', 1,'pcs'
)
SELECT REPLACE(RWID,'2/1',CAST(Rnk AS VARCHAr(2))) AS RWID
,RWLEN
,DESCR
,QTY
,UNIT
FROM
(
SELECT C.*,ROW_NUMBER()OVER(PARTITION BY C.RWID ORDER BY C.RWID) AS Rnk
FROM CTE C
CROSS APPLY CTE C2
)dt WHERE Rnk<3
Result
http://www.sqlfiddle.com/#!18/9eecb/11626

Rank consecutive null values

I want to rank consecutive null value for my records. Every record will be rank as 1. For the null value that only appear once, the rank will also be 1. But for the null values that appear in a consecutive way, the rank will be 1 for the first record and 2 for the second record and so on. Here's my code.
CREATE TABLE #my_table
(
id BIGINT IDENTITY PRIMARY KEY
,fruit varchar(100)
);
INSERT INTO #my_table
SELECT 'apple'
UNION ALL SELECT 'apple'
UNION ALL SELECT NULL
UNION ALL SELECT 'pineapple'
UNION ALL SELECT 'banana'
UNION ALL SELECT NULL
UNION ALL SELECT NULL
UNION ALL SELECT 'orange'
select * from #my_table
Intended result
+----+-----------+------+
| id | fruit | rank |
+----+-----------+------+
| 1 | apple | 1 |
| 2 | apple | 1 |
| 3 | NULL | 1 |
| 4 | pineapple | 1 |
| 5 | banana | 1 |
| 6 | NULL | 1 |
| 7 | NULL | 2 |
| 8 | orange | 1 |
+----+-----------+------+
How should I query it?
Please help!
You can use difference of ROW_NUMBER to get the grouping of consecutive NULL values:
WITH Cte AS(
SELECT *,
g = ROW_NUMBER() OVER(ORDER BY id)
- ROW_NUMBER() OVER(PARTITION BY fruit ORDER BY id)
FROM #my_table
)
SELECT
id,
fruit,
CASE
WHEN fruit IS NULL THEN ROW_NUMBER() OVER(PARTITION BY fruit, g ORDER BY id)
ELSE 1
END AS rank
FROM Cte
ORDER BY id;
ONLINE DEMO
CREATE TABLE #my_table
(
id BIGINT IDENTITY PRIMARY KEY
,fruit varchar(100)
);
INSERT INTO #my_table
SELECT 'apple'
UNION ALL SELECT 'apple'
UNION ALL SELECT NULL
UNION ALL SELECT 'pineapple'
UNION ALL SELECT 'banana'
UNION ALL SELECT NULL
UNION ALL SELECT NULL
UNION ALL SELECT 'orange'
;
WITH REC_CTE (id,fruit,ranks)
AS (
-- Anchor definition
SELECT id,
fruit,
1 as ranks
FROM #my_table
WHERE fruit is not null
-- Recursive definition
UNION ALL
SELECT son.id,
son.fruit,
case when son.fruit is null AND father.fruit is null then
father.ranks + 1
else
1
end as ranks
FROM #my_table son INNER JOIN
REC_CTE father
on son.id = father.id +1
WHERE son.fruit is null
--AND father.fruit is null
)
SELECT * from REC_CTE order by id
DROP TABLE #my_table
Following solution doesn't use recursion (limited to 32767 level = ~ rows depending on solution) and also it uses only two agregate/ranking functions (SUM and DENSE_RANK):
;WITH Base
AS (
SELECT *, IIF(fruit IS NULL, SUM(IIF(fruit IS NOT NULL, 1, 0)) OVER(ORDER BY id), NULL) AS group_num
FROM #my_table t
)
SELECT *, IIF(fruit IS NULL, DENSE_RANK() OVER(PARTITION BY group_num ORDER BY id), 1) rnk
FROM Base b
ORDER BY id
Results:
id fruit group_num rnk
--- --------- --------- ---
100 apple NULL 1
125 apple NULL 1
150 NULL 2 1
175 pineapple NULL 1
200 banana NULL 1
225 NULL 4 1
250 NULL 4 2
275 orange NULL 1
300 NULL 5 1
325 NULL 5 2
350 NULL 5 3

If any data duplicate then output show 1 if not then show 0 in SQL Server

Table: emp
id | name | sal
----------------
1 | abc | 100
2 | ha | 200
1 | abc | 100
1 | abc | 100
1 | abc | 100
2 | ha | 200
2 | ha | 200
3 | hai | 400
Based on this data I want give data duplicate or not in the table for that status i out show output.
I tried like this:
select
count(*) as status
from
[Test].[dbo].[emp]
group by
[id], [name], [sal]
having
count(*) >= 1
order by
count(*) desc
I get this output:
status
4
3
1
I do not want get output like above way.
I want show output like below
Status
1
when data comes unique in table that time status shows : 0 values.
1 means duplicate data and o means unique records.please tell me how to get singe status values to achive this issue.
Try This,
SELECT Id, Name, Sal, COUNT(*) ,
CASE WHEN COUNT(*) > 1 THEN 1
ELSE 0
END Status
FROM
(
SELECT 1 Id, 'abc' Name, 100 Sal
UNION ALL
SELECT 2, 'ha', 200
UNION ALL
SELECT 1, 'abc', 100
UNION ALL
SELECT 1, 'abc', 100
UNION ALL
SELECT 1, 'abc', 100
UNION ALL
SELECT 2, 'ha', 200
UNION ALL
SELECT 2, 'ha', 200
UNION ALL
SELECT 3, 'hai', 400
) A
GROUP BY Id, Name, Sal
select case when c>0 then 1 else 0 end as status from(
select count(*) as c from
(select count(*) as cout from loss
group by loss_claim,loss_key
having count(*)>1)as a) b

How Can I Order in an SQL Statement?

I have these tables: Stock, Unit, Location, Category, StockBalance
At StockBalance: there is StockID from Stock, UnitId from Unit, LocationID from Location
I save at StockBalance Table like following
StockBalanceID | StockID | UnitID | LocationID | BalanceQuantity
1 | 1 | 1 | 1 | 20
2 | 1 | 2 | 1 | 30
3 | 1 | 3 | 1 | 40
4 | 2 | 1 | 2 | 20
5 | 2 | 2 | 2 | 30
6 | 2 | 3 | 2 | 40
I would like to show on Classic ASP as :
Group By : CategoryName
Stock Name Quantity Location Name
Qty | Unit | Qty | Unit | Qty | Unit
Stock One | 20 | One | 30 | Two | 40 | Three | Location One
Stock Two | 20 | One | 30 | Two | 40 | Three | Location Two
How can I Select From StockBalance to get like above? How about my edit one?
Please help me !
PIVOT is what you want.
First some sample data setup:
create table yourTable (
StockBalanceID int, StockID int, UnitID int, LocationID int, BalanceQuantity int);
insert yourTable
select 1 , 1 , 1 , 1 , 20
union all select 2 , 1 , 2 , 1 , 30
union all select 3 , 1 , 3 , 1 , 40
union all select 4 , 2 , 1 , 2 , 20
union all select 5 , 2 , 2 , 2 , 30
union all select 6 , 2 , 3 , 2 , 40
;
Now to do the work...
select StockID, LocationID,
sum(case UnitID when 1 then BalanceQuantity end) as [Unit One],
sum(case UnitID when 2 then BalanceQuantity end) as [Unit Two],
sum(case UnitID when 3 then BalanceQuantity end) as [Unit Three]
from yourTable
group by StockID, LocationID;
Try the following
DECLARE #StockBalances TABLE(
StockBalanceID INT,
StockID INT,
UnitID INT,
LocationID INT,
BalanceQuantity FLOAT
)
DECLARE #Stock TABLE(
StockID INT,
StockName VARCHAR(10)
)
DECLARE #Unit TABLE(
UnitID INT,
UnitName VARCHAR(10)
)
DECLARE #Location TABLE(
LocationID INT,
LocationName VARCHAR(10)
)
INSERT INTO #StockBalances SELECT 1,1,1,1,20
INSERT INTO #StockBalances SELECT 2,1,2,1,30
INSERT INTO #StockBalances SELECT 3,1,3,1,40
INSERT INTO #StockBalances SELECT 4,2,1,2,20
INSERT INTO #StockBalances SELECT 5,2,2,2 ,30
INSERT INTO #StockBalances SELECT 6,2,3,2,40
INSERT INTO #Stock SELECT 1, 'Stock 1'
INSERT INTO #Stock SELECT 2, 'Stock 2'
INSERT INTO #Unit SELECT 1, 'Unit 1'
INSERT INTO #Unit SELECT 2, 'Unit 2'
INSERT INTO #Unit SELECT 3, 'Unit 3'
INSERT INTO #Location SELECT 1, 'Location 1'
INSERT INTO #Location SELECT 2, 'Location 2'
SELECT *
FROM (
SELECT s.StockName,
sb.BalanceQuantity,
u.UnitName,
l.LocationName
FROM #StockBalances sb INNER JOIN
#Stock s ON sb.StockID = s.StockID INNER JOIN
#Unit u ON sb.UnitID = u.UnitID INNER JOIN
#Location l ON sb.LocationID = l.LocationID
) t
PIVOT (SUM(BalanceQuantity) FOR UnitName IN ([Unit 1], [Unit 2], [Unit 3])) p

Resources