T-SQL - Flagging Common Values in One Column Based on ID and updating the database - sql

Problem Statement:
There are Two Columns, VariableName and ID. I want to map variables in the same column with a two-character alpha code such as 'aa', 'ab', 'ac', 'ad'.... up to 'zz' in T-SQL.
To Illustrate,
Input Data:
VariableName ID MapFlag isMapped
var_1 1 NULL FALSE
var_2 1 NULL FALSE
var_3 1 NULL FALSE
var_4 1 NULL FALSE
var_5 1 NULL FALSE
var_1 2 NULL FALSE
var_2 2 NULL FALSE
var_3 2 NULL FALSE
var_10 2 NULL FALSE
var_11 2 NULL FALSE
var_1 3 NULL FALSE
var_10 3 NULL FALSE
var_3 3 NULL FALSE
var_7 3 NULL FALSE
var_1 4 NULL FALSE
var_2 4 NULL FALSE
var_4 4 NULL FALSE
Desired Output
VariableName ID MapFlag isMapped
var_1 1 aa TRUE
var_2 1 ab TRUE
var_3 1 ac TRUE
var_4 1 ad TRUE
var_5 1 NULL FALSE
var_1 2 aa TRUE
var_2 2 ab TRUE
var_3 2 ac TRUE
var_10 2 ae TRUE
var_11 2 NULL FALSE
var_1 3 aa TRUE
var_10 3 ae TRUE
var_3 3 ac TRUE
var_7 3 NULL FALSE
var_1 4 aa TRUE
var_2 4 ab TRUE
var_4 4 ad TRUE
As you can see above,
"var_1" , is present in ids 1, 2, 3, 4 and is tagged as 'aa'
"var_2" , is present in ids 1, 2 and 4 and is tagged as 'ab'
"var_3" , is present in ids 1, 2 and 3 and is tagged as 'ac'
"var_4" , is present in ids 1, and 4 and is tagged as 'ad'
"var_5" , is present in id 1 BUT IS NOT PRESENT IN THE REST OF THE
IDs, so hence NULL
"var_7" , is present in id 3 BUT IS NOT PRESENT IN THE REST OF THE
IDs, so hence NULL
"var_10" , is present in ids 2 and 3 and is tagged as 'ae'
"var_11" , is present in id 2 BUT IS NOT PRESENT IN THE REST OF THE
IDs, so hence NULL
To summarize: Essentially, I want to create a mapping flag with the pattern above 'aa', 'ab', 'ac'
among different IDs. If the mapFlag is NOT NULL, then isMapped TRUE else FALSE
Lastly, I want to write an "Update" stored procedure to convert the input "MapFlag" column with the Input Data and then update the table with the Output data

This is the query to get var marks.
select VariableName, char(ASCII('a')+rn/(ASCII('z') - ASCII('a') +1)) + char(ASCII('a') -1 + rn%(ASCII('z') - ASCII('a') +1)) y
from (
select VariableName, row_number() over (order by VariableName) rn
from mytable
group by VariableName
having count(distinct id) > 1
) t

Related

Get rows with a mix of specific values or null if value does not exist

I have a table that looks like this.
id
value
text
type
eqPath
enabled
1
0
Unknown
NULL
NULL
True
2
1
Production
Production
NULL
True
3
2
Idle
Idle
NULL
True
4
3
Maintenance
Maintenance
NULL
True
5
4
Changeover
Changeover
NULL
True
6
5
Cleaning
Cleaning
NULL
True
7
6
Engineering
Other
NULL
True
8
7
Training
Other
NULL
True
9
8
Pause
Idle
NULL
True
10
1
Running
Production
'Seattle/Sorting/Line 1'
True
11
1
Running
Production
'Seattle/Sorting/Line 2'
True
12
5
Washing
Cleaning
'Seattle/Sorting/Line 2'
False
13
10
Locked Out
Maintenance
'Seattle/Sorting/Line 2'
False
When I use this code, I get back all of the rows with an eqPath of NULL.
SELECT *
FROM modes
WHERE eqPath IS NULL
id
value
text
type
eqPath
enabled
1
0
Unknown
NULL
NULL
True
2
1
Production
Production
NULL
True
3
2
Idle
Idle
NULL
True
4
3
Maintenance
Maintenance
NULL
True
5
4
Changeover
Changeover
NULL
True
6
5
Cleaning
Cleaning
NULL
True
7
6
Engineering
Other
NULL
True
8
7
Training
Other
NULL
True
9
8
Pause
Idle
NULL
True
Now I need one that returns the same table, but if the user requests a specific eqPath, then it would also show up in the list, BUT the value column has to be unique with the row containing the eqPath to replace the row with the NULL
eqPath.
id
value
text
type
eqPath
enabled
1
0
Unknown
NULL
NULL
True
11
1
Running
Production
'Seattle/Sorting/Line 2'
True
3
2
Idle
Idle
NULL
True
4
3
Maintenance
Maintenance
NULL
True
5
4
Changeover
Changeover
NULL
True
12
5
Washing
Cleaning
'Seattle/Sorting/Line 2'
False
7
6
Engineering
Other
NULL
True
8
7
Training
Other
NULL
True
9
8
Pause
Idle
NULL
True
13
10
Locked Out
Maintenance
'Seattle/Sorting/Line 2'
False
This is the code I am trying, but I realize it is wrong.
SELECT DISTINCT *
FROM modes
WHERE eqPath = ?
OR eqPath IS NULL
GROUP BY value
You can use ROW_NUMBER to mark the rows and use it in filter to get the expected result.
A sql query will be something like -
select id, value, text, type, eqPath, enabled
from
(select * ,
row_number() over( partition by value order by eqPath desc) rn
from <your-table>
where (eqPath is null or eqPath = ?) ) tbl
where rn = 1
order by value
the row_number in the query will put a row number for each rows and resets when the value changes, so finally those rows with row number matching 1 will the ones we need to fetch.
here is a fiddle
I think I came up with a solution:
SELECT *
FROM modes
WHERE eqPath IS NULL
AND value NOT IN (
SELECT value
FROM modes
WHERE eqPath = ?
)
UNION
SELECT *
FROM modes
WHERE eqPath = ?
ORDER BY value

SQL to fetch data using column values from one table as columns of another table

I have these two tables:
Org_Extra_Attr
org_id attr_name attr_path
1 desk_name str1
1 citizen bool1
2 perm_user bool1
3 skype_id str1
3 twitter str2
User_Attr_Values
org_id user_id str1 str2 str3 str4 bool1 bool2 bool3 bool4
1 1 b1d07 null null null 1 null null null
1 2 b2d01 null null null 0 null null null
2 3 null null null null 1 null null null
2 4 null null null null 1 null null null
3 5 sam_sky sam_twt null null null null null null
3 6 tom_sky tom_twt null null null null null null
So, the thing here is each org.can define max.of 4 extra attributes of type String and Boolean each, the Org_Extra_Attr table is like meta-data. For example org_id 1 has defined desk_name which will be str1's value for its users, whereas org_id 3 has skype_id which will be str1's value for its users.
This might be a bad design, but for now, I need to get users attribute names and values for a given org_id. Like for org_id = 1, I need a SQL query result(not a third table) like below:
user_id attr_name val
1 desk_name b1d07
1 citizen 1
2 desk_name b2d01
2 citizen 0
For org_id = 3
user_id attr_name val
5 skype_id sam_sky
5 twitter sam_twt
6 skype_id tom_sky
6 twitter tom_twt
Something like this
select
t2.user_id,
t1.attr_name,
CASE
WHEN t1.attr_path='str1' then t2.str1
WHEN t1.attr_path='str2' then t2.str2
WHEN t1.attr_path='str3' then t2.str3
WHEN t1.attr_path='str4' then t2.str4
WHEN t1.attr_path='bool1' then t2.bool1
WHEN t1.attr_path='bool2' then t2.bool2
WHEN t1.attr_path='bool3' then t2.bool3
WHEN t1.attr_path='bool4' then t2.bool4
END attr_value
FROM org_Extra_attr t1 inner join User_Attr_Values t2
on t1.org_id = t2.org_id
where t1.org_id=1
This is not an answer, but too long for a comment
That is a bad data model. You should not store column names in your tables.
What you could have instead is:
Org_Attr (PK = org_id + attr_no)
org_id attr_no attr_name type
1 1 desk_name STRING
1 2 citizen BOOL
2 1 perm_user BOOL
3 1 skype_id STRING
3 2 twitter STRING
Org_Attr_User (PK = org_id + attr_no + user_id)
org_id attr_no user_id value
1 1 1 b1d07
1 1 2 b2d01
1 2 1 1
1 2 2 0
2 1 3 1
2 1 4 1
3 1 5 sam_sky
3 1 6 tom_sky
3 2 5 sam_twt
3 2 6 tom_twt
With such a model data integrity would be guaranteed and querying, too, would be simple:
select oau.user_id, oa.attr_name, oau.value
from Org_Attr oa
join Org_Attr_User oau using (org_id, attr_no)
where org_id = 1;

sql server count distinct value of a field

I have 2 tables : Contents and Packs
In Content columns are like this:
Id Name IsPerishable IsSpecial IsDanger
----------------------------------------------------------
1 Paper False False False
3 Fruit True False False
5 NewsPaper False True False
6 Radioactive False False True
7 Foods True False False
In Packs columns are like this:
Id From To ContentId
---------------------------------------
1 ABZ ATL 3
2 ANU ATL 5
3 BAQ ATL 7
4 BTS BAQ 6
5 FRL BAQ 5
Now I want a result that groups every 'To' then shows separate value of IsPerishable / IsSpecial / IsDanger
like this:
To Perishable Special Danger
-----------------------------------------------
ATL 2 1 0
BAQ 0 1 1
I try use some query but none of them has worked correctly:
select Name,
case
when IsSpecial = 1 then count(IsSpecial)
when IsPerishable = 1 then count(IsPerishable)
when IsDanger = 1 then count(IsDanger)
end as 'Content'
from Contents
group by IsSpecial , IsPerishable , IsDanger
select To,count(DISTINCt Id) as 'Count'
FROM Packs
GROUP BY To
try this
Select To,
(Select count(*) from Content c where c.Id= p.Id and c.IsSpecial=1) as Special,
(Select count(*) from Content c where c.Id= p.Id and c.Isperishable=1) as Perishable,
(Select count(*) from Content c where c.Id= p.Id and c.Isperishable=1) as Danger
from
pack p
group by To
SELECT P.To, SUM(Perishable), SUM(Special),SUM(Danger) FROM
(
SELECT Id,
CASE IsPerishable WHEN true THEN 1 ELSE 0 END AS Perishable,
CASE IsSpecial WHEN true then 1 ELSE 0 END AS Special,
CASE IsDanger WHEN true then 1 ELSE 0 END AS Danger
FROM Contents
) C
Right Join Packs P
on P.ContentId=C.Id
Group BY P.to

Split single column to multiple

My table look like this:
id customer_code comments
-----------------------------------
1 Dilbert true
2 Dilbert false
3 Wally true
4 wally true
5 wally false
I need to results look like this:
id customer_code x1 x2
------------------------------
1 Dilbert ture
2 Dilbert false
3 wally ture
4 wally ture
5 wally false
SELECT [id], [customer_code],
CASE [comments] WHEN 'TRUE' THEN 'TRUE' ELSE NULL END AS X1,
CASE [comments] WHEN 'FALSE' THEN 'FALSE' ELSE NULL END AS X2
FROM Table1;
FIDDLE

Value carry forword

I have below SQL Query..
SELECT dbo.O3.[s.no] AS [O3_Sn.NO], dbo.O5.[s.no] AS [O5_Sn.NO], dbo.O3.O3, dbo.O5.O5,
case when dbo.O3.[s.no] > dbo.O5.[s.no] then 'true' else 'false' end as ComparisonColumn
FROM dbo.O3 INNER JOIN
dbo.O5 ON dbo.O3.[s.no] = dbo.O5.[s.no]
When I run I am getting below output..
O3_Sn.NO O5_Sn.NO O3 O5 ComparisonColumn
1 1 10 11 TRUE
2 2 12 13 TRUE
3 3 11 10 FALSE
4 4 13 11 FALSE
5 5 15 16 TRUE
6 6 10 11 TRUE
7 7 12 13 TRUE
I want to remember the value of TRUE / False and should ignore if it is repeated untill i get a reverse case i.e., for TRUE , False.. and FOR False .. TRUE
Below is the out i should get it..
O3_Sn.NO O5_Sn.NO O3 O5 ComparisonColumn New_Case_Carry_value
1 1 10 11 TRUE TRUE
2 2 12 13 TRUE NULL
3 3 11 10 FALSE FALSE
4 4 13 11 FALSE NULL
5 5 15 16 TRUE TRUE
6 6 10 11 TRUE NULL
7 7 12 13 TRUE NULL
You can order your output using row_number() to compare a value with a value of the previous record:
with cIntermediate as (
SELECT dbo.O3.[s.no] AS [O3_Sn.NO], dbo.O5.[s.no] AS [O5_Sn.NO], dbo.O3.O3, dbo.O5.O5,
case when dbo.O3.[s.no] > dbo.O5.[s.no] then 'true' else 'false' end
as ComparisonColumn,
rowno = row_number() over
(order by dbo.O3.[s.no], dbo.O5.[s.no], dbo.O3.O3, dbo.O5.O5)
FROM dbo.O3 INNER JOIN dbo.O5 ON dbo.O3.[s.no] = dbo.O5.[s.no]
)
select i1.*,
case when i1.ComparisonColumn = i2.ComparisonColumn then null
else i1.ComparisonColumn end as NewCaseCarryValue
from cIntermediate i1
left join cIntermediate i2 on i2.rowno=i1.rowno-1
Ok, I will explain the concept to you, and I am sure you will be able to figure it out for yourself.
Your final result you published with all the true and false columns, that needs to become a temporary table, like this, but with some kind of identity column:
SELECT dbo.o3.[s.no] AS [O3_Sn.NO]
,dbo.o5.[s.no] AS [O5_Sn.NO]
,dbo.o3.o3
,dbo.o5.o5
,CASE
WHEN dbo.o3.[s.no] > dbo.o5.[s.no] THEN 'true'
ELSE 'false'
END AS comparisoncolumn
, ROW_NUMBER() OVER(ORDER BY dbo.o3.[s.no]) AS ident_col
INTO #temp
FROM dbo.o3
INNER JOIN dbo.o5
ON dbo.o3.[s.no] = dbo.o5.[s.no]
Then what you need to do is to select from #temp, and self join to #temp in a similar manner as here:
SELECT a.*
, CASE WHEN a.comparisoncolumn = b.comparisoncolumn THEN NULL ELSE a.comparisoncolumn END AS final_comparisoncolumn
FROM #temp a
LEFT JOIN #temp b ON a.ident_col = b.ident_col - 1
Then do a case statement to figure out if you need to print null, or true or false.
Play around with this concept, I am sure you will be able to work it out from here.
You can do an left join on the table itself (SN = SN-1) and compare the ComparisonColumn