TSQL - Help with UNPIVOT - sql

I am transforming data from this legacy table:
Phones(ID int, PhoneNumber, IsCell bit, IsDeskPhone bit, IsPager bit, IsFax bit)
These bit fields are not nullables and, potentially, all four bit fields can be 1.
How can I unpivot this thing so that I end up with a separate row for each bit field = 1. For instance, if the original table looks like this...
ID, PhoneNumber, IsCell, IsPager, IsDeskPhone, IsFax
----------------------------------------------------
1 123-4567 1 1 0 0
2 123-6567 0 0 1 0
3 123-7567 0 0 0 1
4 123-8567 0 0 1 0
... I want the result to be the following:
ID PhoneNumber Type
-----------------------
1 123-4567 Cell
1 123-4567 Pager
2 123-6567 Desk
3 123-7567 Fax
4 123-8567 Desk
Thanks!

2005/2008 version
SELECT ID, PhoneNumber, Type
FROM
(SELECT ID, PhoneNumber,IsCell, IsPager, IsDeskPhone, IsFax
FROM Phones) t
UNPIVOT
( quantity FOR Type IN
(IsCell, IsPager, IsDeskPhone, IsFax)
) AS u
where quantity = 1
see also Column To Row (UNPIVOT)

Try this:
DROP TABLE #Phones
CREATE TABLE #Phones
(
Id int,
PhoneNumber varchar(50),
IsCell bit,
IsPager bit,
IsDeskPhone bit,
IsFax bit
)
INSERT INTO #Phones VALUES (1, '123-4567', 1, 1, 0, 0)
INSERT INTO #Phones VALUES (2, '123-6567', 0, 0, 1, 0)
INSERT INTO #Phones VALUES (3, '123-7567', 0, 0, 0, 1)
INSERT INTO #Phones VALUES (4, '123-8567', 0, 0, 1, 0)
SELECT Id, PhoneNumber, [Type]
FROM (
SELECT Id, PhoneNumber,
Cell = IsCell, Pager = IsPager,
Desk = IsDeskPhone, Fax = IsFax
FROM #Phones
) a
UNPIVOT(
something FOR [Type] IN (Cell, Pager, Desk, Fax )
) as upvt

Related

Choose a record from the table where two of the default values are 0 or 1, a bit tricky

Looking for a more elegant and most logical solution for:
The table:
index
id
by_default
text
1
1
0
AAA
2
1
1
ABA
3
1
0
ABC
4
2
0
BCA
5
2
0
BCB
The task is to find the minimum index value with defaults set to 1 and/or defaults set to 0.
I have the following code (not very elegant, but it works, also very slow):
declare #byd_1 as int=
(select min(t.index) idx from Table t where t.[id]=1 and t.by_default=1)
declare #byd_2 as int=
(select min(t.index) idx from Table t where t.[id]=1 and t.by_default=0)
select (case when #byd_1 is null then #byd_2 else #byd_1 end)
The tricky part is: sometimes the by_default column is always 0 (for example: id:2 may have no by_default values set) and as mentioned earlier the task is: need to get the minimum value of the index column.
What is the most elegant (one-line) code possible?
Using MSSQL
The expected results, according to the sample table, should be the following:
index
id
by_default
text
2
1
1
ABA
4
2
0
BCA
Edited to add text also.
A bit ugly but:
drop table #t
select *
into #t
from (
VALUES (1, 1, 0, N'AAA')
, (2, 1, 1, N'ABA')
, (3, 1, 0, N'ABC')
, (4, 2, 0, N'BCA')
, (5, 2, 0, N'BCB')
) t (index_,id,by_default,text)
select index_, id, by_default, text
from (
select min(index_) OVER(PARTITION BY id) AS minIndex
, MIN(case when by_default = 1 then index_ end) over(partition by id) AS minIndexDefault
, *
from #t
) t
where isnull(minIndexDefault, minIndex) = index_

Find row with the most true value columns

Table User
estado_id
user_id
shopping
mercado
feira
1
1
1
0
1
1
2
1
1
1
1
3
1
0
1
Table Cidade
estado_id
version_id
name
type
1
1
jose
shopping
1
2
maria
feira
1
2
maria
mercado
1
3
bruna
mercado
I need to compare the states, and bring only those that cover user x criteria's, but the user table may contain hundreds of users and I just have to return to which user x is bound. In the users table, I need to bring everyone that they think matches. If in the shopping and fair table the value is 1 and market to 0 then shopping and fair must be returned within these versions, I managed to bring up only the user data / id_state, however when I compare the user table = 1 with the city table type = shopping
But I cannot find a way to do that, because' it needs to be dynamic
WITH CTE AS (
SELECT cidade.*, uv.user_id, uv.shopping, uv.mercado, uv.feira
FROM cidade
INNER JOIN userst as uv ON cidade.estado_id = uv.estado_id
WHERE cidade.estado_id = 1 AND uv.user_id = 1
)
SELECT [type], shopping, mercado, feira
FROM CTE
My expectation
estado_id
version_id
name
type
user_id
1
1
jose
shopping
1
CREATE TABLE cidade(
estado_id int,
version_id int,
name varchar(255),
[type] varchar(255)
);
CREATE TABLE userst (
estado_id int,
user_id int,
shopping int,
mercado int,
feira int
);
INSERT INTO cidade (estado_id, version_id, name, [type])
VALUES (1, 1, 'jose', 'shopping')
, (1, 2, 'maria', 'feira')
, (1, 2, 'maria', 'mercado')
, (1, 3, 'bruna', 'mercado');
INSERT INTO userst (estado_id, user_id, shopping, mercado, feira)
VALUES (1, 1, 1, 0, 1)
, (1, 2, 1, 1, 1)
, (1, 3, 1, 0, 1);

SQL: Pinned rows and row number calculation

We have a requirement to assign row number to all rows using following rule
Row if pinned should have same row number
Otherwise sort it by GMD
Example:
ID GMD IsPinned
1 2.5 0
2 0 1
3 2 0
4 4 1
5 3 0
Should Output
ID GMD IsPinned RowNo
5 3 0 1
2 0 1 2
1 2.5 0 3
4 4 1 4
3 2 0 5
Please Note row number for Id's 2 and 4 stayed intact as they are pinned with values of 2 and 4 respectively even though the GMD are not in any order
Rest of rows Id's 1, 3 and 5 row numbers are sorted using GMD desc
I tried using RowNumber SQL 2012 however, it is pushing pinned items from their position
Here's a set-based approach to solving this. Note that the first CTE is unnecessary if you already have a Numbers table in your database:
declare #t table (ID int,GMD decimal(5,2),IsPinned bit)
insert into #t (ID,GMD,IsPinned) values
(1,2.5,0), (2, 0 ,1), (3, 2 ,0), (4, 4 ,1), (5, 3 ,0)
;With Numbers as (
select ROW_NUMBER() OVER (ORDER BY ID) n from #t
), NumbersWithout as (
select
n,
ROW_NUMBER() OVER (ORDER BY n) as rn
from
Numbers
where n not in (select ID from #t where IsPinned=1)
), DataWithout as (
select
*,
ROW_NUMBER() OVER (ORDER BY GMD desc) as rn
from
#t
where
IsPinned = 0
)
select
t.*,
COALESCE(nw.n,t.ID) as RowNo
from
#t t
left join
DataWithout dw
inner join
NumbersWithout nw
on
dw.rn = nw.rn
on
dw.ID = t.ID
order by COALESCE(nw.n,t.ID)
Hopefully my naming makes it clear what we're doing. I'm a bit cheeky in the final SELECT by using a COALESCE to get the final RowNo when you might have expected a CASE expression. But it works because the contents of the DataWithout CTE is defined to only exist for unpinned items which makes the final LEFT JOIN fail.
Results:
ID GMD IsPinned RowNo
----------- --------------------------------------- -------- --------------------
5 3.00 0 1
2 0.00 1 2
1 2.50 0 3
4 4.00 1 4
3 2.00 0 5
Second variant that may perform better (but never assume, always test):
declare #t table (ID int,GMD decimal(5,2),IsPinned bit)
insert into #t (ID,GMD,IsPinned) values
(1,2.5,0), (2, 0 ,1), (3, 2 ,0), (4, 4 ,1), (5, 3 ,0)
;With Numbers as (
select ROW_NUMBER() OVER (ORDER BY ID) n from #t
), NumbersWithout as (
select
n,
ROW_NUMBER() OVER (ORDER BY n) as rn
from
Numbers
where n not in (select ID from #t where IsPinned=1)
), DataPartitioned as (
select
*,
ROW_NUMBER() OVER (PARTITION BY IsPinned ORDER BY GMD desc) as rn
from
#t
)
select
dp.ID,dp.GMD,dp.IsPinned,
CASE WHEN IsPinned = 1 THEN ID ELSE nw.n END as RowNo
from
DataPartitioned dp
left join
NumbersWithout nw
on
dp.rn = nw.rn
order by RowNo
In the third CTE, by introducing the PARTITION BY and removing the WHERE clause we ensure we have all rows of data so we don't need to re-join to the original table in the final result in this variant.
this will work:
CREATE TABLE Table1
("ID" int, "GMD" number, "IsPinned" int)
;
INSERT ALL
INTO Table1 ("ID", "GMD", "IsPinned")
VALUES (1, 2.5, 0)
INTO Table1 ("ID", "GMD", "IsPinned")
VALUES (2, 0, 1)
INTO Table1 ("ID", "GMD", "IsPinned")
VALUES (3, 2, 0)
INTO Table1 ("ID", "GMD", "IsPinned")
VALUES (4, 4, 1)
INTO Table1 ("ID", "GMD", "IsPinned")
VALUES (5, 3, 0)
SELECT * FROM dual
;
select * from (select "ID","GMD","IsPinned",rank from(select m.*,rank()over(order by
"ID" asc) rank from Table1 m where "IsPinned"=1)
union
(select "ID","GMD","IsPinned",rank from (select t.*,rank() over(order by "GMD"
desc)-1 rank from (SELECT * FROM Table1)t)
where "IsPinned"=0) order by "GMD" desc) order by rank ,GMD;
output:
2 0 1 1
5 3 0 1
1 2.5 0 2
4 4 1 2
3 2 0 3
Can you try this query
CREATE TABLE Table1
(ID int, GMD numeric (18,2), IsPinned int);
INSERT INTO Table1 (ID,GMD, IsPinned)
VALUES (1, 2.5, 0),
(2, 0, 1),
(3, 2, 0),
(4, 4, 1),
(5, 3, 0)
select *, row_number () over(partition by IsPinned order by (case when IsPinned =0 then GMD else id end) ) [CustOrder] from Table1
This took longer then I thought, the thing is row_number would take a part to resolve the query. We need to differentiate the row_numbers by id first and then we can apply the while loop or cursor or any iteration, in our case we will just use the while loop.
dbo.test (you can replace test with your table name)
1 2.5 False
2 0 True
3 3 False
4 4 True
6 2 False
Here is the query I wrote to achieve your result, I have added comment under each operation you should get it, if you have any difficultly let me know.
Query:
--user data table
DECLARE #userData TABLE
(
id INT NOT NULL,
gmd FLOAT NOT NULL,
ispinned BIT NOT NULL,
rownumber INT NOT NULL
);
--final result table
DECLARE #finalResult TABLE
(
id INT NOT NULL,
gmd FLOAT NOT NULL,
ispinned BIT NOT NULL,
newrownumber INT NOT NULL
);
--inserting to uer data table from the table test
INSERT INTO #userData
SELECT t.*,
Row_number()
OVER (
ORDER BY t.id ASC) AS RowNumber
FROM test t
--creating new table for ids of not pinned
CREATE TABLE #ids
(
rn INT,
id INT,
gmd FLOAT
)
-- inserting into temp table named and adding gmd by desc
INSERT INTO #ids
(rn,
id,
gmd)
SELECT DISTINCT Row_number()
OVER(
ORDER BY gmd DESC) AS rn,
id,
gmd
FROM #userData
WHERE ispinned = 0
--declaring the variable to loop through all the no pinned items
DECLARE #id INT
DECLARE #totalrows INT = (SELECT Count(*)
FROM #ids)
DECLARE #currentrow INT = 1
DECLARE #assigningNumber INT = 1
--inerting pinned items first
INSERT INTO #finalResult
SELECT ud.id,
ud.gmd,
ud.ispinned,
ud.rownumber
FROM #userData ud
WHERE ispinned = 1
--looping through all the rows till all non-pinned items finished
WHILE #currentrow <= #totalrows
BEGIN
--skipping pinned numers for the rows
WHILE EXISTS(SELECT 1
FROM #finalResult
WHERE newrownumber = #assigningNumber
AND ispinned = 1)
BEGIN
SET #assigningNumber = #assigningNumber + 1
END
--getting row by the number
SET #id = (SELECT id
FROM #ids
WHERE rn = #currentrow)
--inserting the non-pinned item with new row number into the final result
INSERT INTO #finalResult
SELECT ud.id,
ud.gmd,
ud.ispinned,
#assigningNumber
FROM #userData ud
WHERE id = #id
--going to next row
SET #currentrow = #currentrow + 1
SET #assigningNumber = #assigningNumber + 1
END
--getting final result
SELECT *
FROM #finalResult
ORDER BY newrownumber ASC
--dropping table
DROP TABLE #ids
Output:

T-SQL Summation

I'm trying to create result set with 3 columns. Each column coming from the summation of 1 Column of Table A but grouped by different ID's. Here's an overview of what I wanted to do..
Table A
ID Val.1
1 4
1 5
1 6
2 7
2 8
2 9
3 10
3 11
3 12
I wanted to create something like..
ROW SUM.VAL.1 SUM.VAL.2 SUM.VAL.3
1 15 21 33
I understand that I can not get this using UNION, I was thinking of using CTE but not quite sure with the logic.
You need conditional Aggregation
select 1 as Row,
sum(case when ID = 1 then Val.1 end),
sum(case when ID = 2 then Val.1 end),
sum(case when ID = 3 then Val.1 end)
From yourtable
You may need dynamic cross tab or pivot if number of ID's are not static
DECLARE #col_list VARCHAR(8000)= Stuff((SELECT ',sum(case when ID = '+ Cast(ID AS VARCHAR(20))+ ' then [Val.1] end) as [val.'+Cast(ID AS VARCHAR(20))+']'
FROM Yourtable
GROUP BY ID
FOR xml path('')), 1, 1, ''),
#sql VARCHAR(8000)
exec('select 1 as Row,'+#col_list +'from Yourtable')
Live Demo
I think pivoting the data table will yield the desired result.
IF OBJECT_ID('tempdb..#TableA') IS NOT NULL
DROP TABLE #TableA
CREATE TABLE #TableA
(
RowNumber INT,
ID INT,
Value INT
)
INSERT #TableA VALUES (1, 1, 4)
INSERT #TableA VALUES (1, 1, 5)
INSERT #TableA VALUES (1, 1, 6)
INSERT #TableA VALUES (1, 2, 7)
INSERT #TableA VALUES (1, 2, 8)
INSERT #TableA VALUES (1, 2, 9)
INSERT #TableA VALUES (1, 3, 10)
INSERT #TableA VALUES (1, 3, 11)
INSERT #TableA VALUES (1, 3, 12)
-- https://msdn.microsoft.com/en-us/library/ms177410.aspx
SELECT RowNumber, [1] AS Sum1, [2] AS Sum2, [3] AS Sum3
FROM
(
SELECT RowNumber, ID, Value
FROM #TableA
) a
PIVOT
(
SUM(Value)
FOR ID IN ([1], [2], [3])
) AS p
This technique works if the ids you are seeking are constant, otherwise I imagine some dyanmic-sql would work as well if changing ids are needed.
https://msdn.microsoft.com/en-us/library/ms177410.aspx

Sql: Select where a condition is true for all types

I have a table defined and populated as follows:
DECLARE #Temp TABLE
(
ProjectId INT,
EmployeeId INT,
SomeTypeId INT,
IsExpired BIT,
IsWarning BIT,
IsIncomplete BIT
)
--all incomplete...
INSERT INTO #Temp VALUES (1, 1, 1, 0, 0, 1)
INSERT INTO #Temp VALUES (1, 1, 2, 0, 0, 1)
INSERT INTO #Temp VALUES (1, 1, 3, 0, 0, 1)
--two warnings...
INSERT INTO #Temp VALUES (1, 2, 1, 0, 1, 0)
INSERT INTO #Temp VALUES (1, 2, 2, 0, 1, 0)
INSERT INTO #Temp VALUES (1, 2, 3, 0, 0, 0)
--two expirations...
INSERT INTO #Temp VALUES (1, 3, 1, 0, 0, 0)
INSERT INTO #Temp VALUES (1, 3, 2, 1, 0, 0)
INSERT INTO #Temp VALUES (1, 3, 3, 1, 0, 0)
I want to return distinct ProjectId, EmployeeId pairs with any warnings or expirations:
SELECT DISTINCT ProjectId, EmployeeId FROM #Temp WHERE IsWarning = 1 OR IsExpired = 1
No problem.
However, I'd like to also return ProjectId, EmployeeId pairs where IsWarning = 0 and IsExpired = 0 and IsIncomplete = 1, but this must be true for all SomeTypeId's (1, 2, 3). In other words, no warnings, no expirations, just incomplete for all categories.
SomeTypeId Fks to a lookup table. Right now there are only 3 entries, but there could be more in the future.
Any ideas?
I would expect ProjectId, EmployeeId (1, 1) to be returned.
This uses MAX and MIN to see to that all values are the same; one needs to add a + 0 to each bit to make it available to normal numeric aggregate functions (like MIN/MAX/SUM/...)
SELECT ProjectId, EmployeeId
FROM Temp
GROUP BY ProjectId, EmployeeId
HAVING MAX(IsWarning + 0) = 0
AND MAX(IsExpired + 0) = 0
AND MIN(IsIncomplete + 0) = 1
An SQLfiddle for testing.
I'm not sure from your question if you mean that you want those results in the SAME query, or you want an entirely new query. Assuming the latter, your query would be:
SELECT DISTINCT ProjectId, EmployeeId FROM #Temp WHERE (IsWarning = 0 AND IsExpired = 0 AND IsIncomplete = 1)
Assuming the former, it would be:
SELECT DISTINCT ProjectId, EmployeeId FROM #Temp WHERE (IsWarning = 1 OR IsExpired = 1)
OR (IsWarning = 0 AND IsExpired = 0 AND IsIncomplete = 1)
Try this assuming you have no duplicate records;
Please note that union is added assuming you still need to select data WHERE IsWarning = 1 OR IsExpired = 1. If you don't need them simply remove it.
SQL-DEMO HERE
;with cte as (
select projectid, employeeid,
case when sometypeid in (1,2,3) and
IsWarning = 0 and IsExpired = 0 and IsIncomplete = 1
then 1 else 0 end x
from temp
)
select projectid, employeeid
from cte
group by projectId, employeeid
having sum(x) >= 3
union
select projectid, employeeid
from Temp
where IsWarning = 1 or IsExpired = 1