SQL for the below requirements - sql

I am having trouble framing a SQL to get the desired outputs.
Table X
Id_X | GroupId | SomeColumn
Table R
Id_R | Id_X | GroupId | RColumn
The objective is to pick Id_X from Table R that have only GroupId values (A,B) and RColumn value is RValue
Ex:
Table X
1 | A | SomeValue
1 | B | SomeValue
2 | A | SomeValue
2 | B | SomeValue
2 | B | SomeValue
2 | C | SomeValue
Table R
101 | 1 | A | RValue
102 | 2 | A | RValue
The SQL should return 1

SELECT
[X].Id_X
FROM
[R]
INNER JOIN [X] ON
[R].Id_X = [X].Id_X
AND
[R].GroupId = [X].GroupId
WHERE
[X].GroupId IN ( 'A', 'B' )
AND
[R].RColumn = 'RValue'

If i understand correctly, your query should be
DECLARE #TableX AS TABLE
(
Id_X int, GroupId varchar(10), SomeColumn varchar(20)
)
INSERT INTO #TableX
VALUES
( 1, 'A', 'SomeValue'),
( 1, 'B', 'SomeValue'),
( 2, 'A', 'SomeValue'),
( 2, 'B', 'SomeValue'),
( 2, 'B', 'SomeValue'),
( 2, 'C', 'SomeValue')
DECLARE #TableR AS TABLE
(
ID_R int, Id_X int, GroupId varchar(10),RColumn varchar(10)
)
INSERT INTO #TableR
VALUES (101,1,'A','RValue'), (102,2,'A','RValue')
SELECT DISTINCT tr.Id_X
FROM #TableR tr
INNER JOIN #TableX tx ON tx.Id_X = tr.Id_X AND tx.GroupId = tr.GroupId
WHERE tr.RColumn = 'RValue'
AND NOT EXISTS ( SELECT 1 FROM #TableX tx2
WHERE tx2.Id_X = tx.Id_X
AND tx2.GroupId NOT IN ('A','B')
)
Demo link: http://rextester.com/EGLOT75874

Related

Update the Unique number for the co-related records between two columns in the group

I need to identify and update co-related records associated rank under Req_Result column as depicted below.
Table name is tblSource.
+------+-----+-----------------+---------+
| Item | key | DenseRankWrtKey | Req_Res |
+------+-----+-----------------+---------+
| a | 1 | 1 | 1 |
+------+-----+-----------------+---------+
| a | 2 | 2 | 1 |
+------+-----+-----------------+---------+
| a | 3 | 3 | 1 |
+------+-----+-----------------+---------+
| b | 2 | 2 | 1 |
+------+-----+-----------------+---------+
| b | 9 | 7 | 1 |
+------+-----+-----------------+---------+
| c | 1 | 1 | 1 |
+------+-----+-----------------+---------+
| c | 6 | 5 | 1 |
+------+-----+-----------------+---------+
| d | 5 | 4 | 4 |
+------+-----+-----------------+---------+
| e | 8 | 6 | 6 |
+------+-----+-----------------+---------+
| f | 2 | 2 | 1 |
+------+-----+-----------------+---------+
| f | 6 | 5 | 1 |
+------+-----+-----------------+---------+
Item and Key are co-related columns and DenseRankWrtKey is created by using Dense rank with respect to key. I need to assign the same DenseRankWrtKey values to all the co-related values.
Scenario explained:
Item a has the key value 1 and 1 is co-related with c as well, so all related values for a and 1 are a,b,c,f,2,3,7,6,5 hence all these values are assigned as 1 by referring DenseRank column, d and e are not further related to any other values hence its value is kept as is from DenseRank column.
I tried the queries
Update a
SET a.Req_Res = b.DenseRankWrtKey
from tblSource a
inner join tblSource b on a.DenseRankWrtKey = b.DenseRankWrtKey
which is not sufficient.
Just try for this table too : DECLARE #Table AS TABLE
(
Id INT IDENTITY(1,1) PRIMARY KEY
,Item varchar(100)
,[key] INT
,DenseRankWrtKey INT
,Req_Res INT
)
INSERT INTO #Table
(
Item
,[key]
,DenseRankWrtKey
)
VALUES
('p', 10 ,1 ),
('q', 10 ,1 ),
('r', 20 ,2 ),
('s', 30 ,3 ),
('t', 30 ,3 ),
('u', 40 ,4 ),
('v', 40 ,4 ),
('w', 40 ,4 ),
('p', 50 ,5 ),
('q', 50 ,5 ),
('r', 50 ,5 ),
('s', 50 ,5 ),
('t', 50 ,5 ),
('u', 50 ,5 ),
('v', 50 ,5 ),
('w', 50 ,5 )
I find this way easier to read and maintain
DECLARE #TestTable TABLE (Item CHAR(1), ItemKey INT, DenseRankWrtKey INT, Req_Res INT)
INSERT #TestTable (Item, ItemKey, DenseRankWrtKey) VALUES
('a' , 1 , 1)
, ('a' , 2 , 2)
, ('a' , 3 , 3)
, ('b' , 2 , 2)
, ('b' , 9 , 7)
, ('c' , 1 , 1)
, ('c' , 6 , 5)
, ('d' , 5 , 4)
, ('e' , 8 , 6)
, ('f' , 2 , 2)
, ('f' , 6 , 5)
DECLARE #OtpTable TABLE (Item CHAR(1), ItemKey INT, DenseRankWrtKey INT)
DECLARE #RC INT = 1
WHILE #RC > 0
BEGIN
DELETE #OtpTable
;WITH UpdateCTE AS (
SELECT TOP 1 * from #TestTable
WHERE Req_Res IS NULL
)
UPDATE UpdateCTE
set Req_Res = DenseRankWrtKey
OUTPUT Inserted.Item, Inserted.ItemKey, inserted.DenseRankWrtKey INTO #OtpTable
SET #RC = ##ROWCOUNT
WHILE ##ROWCOUNT > 0
UPDATE T
SET Req_Res = (SELECT TOP 1 DenseRankWrtKey FROM #OtpTable)
OUTPUT Inserted.Item, Inserted.ItemKey, inserted.DenseRankWrtKey INTO #OtpTable
FROM #TestTable T
WHERE T.Req_Res IS NULL AND EXISTS (SELECT 1 FROM #OtpTable OT WHERE (T.Item = OT.Item OR T.ItemKey = OT.ItemKey))
END
SELECT * FROM #TestTable
You can not do update in single statement.
CREATE TABLE #Table
(
Id INT
,Item varchar(30)
,[key] INT
,DenseRankWrtKey INT
,Req_Res INT
)
INSERT INTO #Table
(
Item
,[key]
,DenseRankWrtKey
)
VALUES
<YOUR DATA>
;WITH CTE
AS
(
SELECT
T.Item
,T.[Key]
,Id = RANK() OVER(order by T.DenseRankWrtKey,T.Item)
FROM
#Table AS T
)
UPDATE
T
SET
T.Id = CTE.Id
FROM
CTE
INNER JOIN #Table AS T ON T.Item = CTE.Item AND T.[key] = CTE.[key]
DECLARE #LoopVal INT = 0
,#LoopReq INT = NULL
,#LoopKey VARCHAR(50) = NULL
WHILE 1 = 1
BEGIN
SELECT TOP 1
#LoopVal = T.DenseRankWrtKey
,#LoopReq = T.Req_Res
FROM
#Table AS T
WHERE
T.DenseRankWrtKey > #LoopVal
ORDER BY
T.DenseRankWrtKey ASC
IF ##ROWCOUNT = 0
BREAK;
UPDATE T2
SET Req_Res = CASE WHEN #LoopReq IS NOT NULL THEN #LoopReq ELSE T.DenseRankWrtKey END
FROM
#Table AS T
INNER JOIN #Table AS T2 ON T.[key] = T2.[key]
WHERE
T.DenseRankWrtKey = #LoopVal
AND T2.Req_Res IS NULL
UPDATE
T
SET
T.Req_Res = CASE WHEN #LoopReq IS NOT NULL THEN #LoopReq ELSE T2.Req_Res END
FROM
#Table AS T
INNER JOIN #Table AS T2 ON T.Item = T2.Item
AND T2.Req_Res IS NOT NULL
AND T.Req_Res IS NULL
END
SELECT * FROM #Table
ORDER BY
DenseRankWrtKey
DROP TABLE #Table
GO

SQL server and STUFF with two tables

I'm facing a problem. I have two tables as below.
table 1
+----+------+
| ks | keys |
+----+------+
| 11 | 1122|
+----+------+
| 12 | 2211|
+----+------+
| 13 | 2233|
+----+------+
| 14 | 3322|
+----+------+
table 2
+----+--+-------+
| Id | ks|codes|
+----+-----------+
| 1 | 11 |aaaaa|
+----+-----------+
| 2 | 11 |bbbbb|
+----+-----------+
| 3 | 12 |aaaaa|
+----+-----------+
| 3 | 13 |ccccc|
+----+-----------+
| 4 | 12 |bbbbb|
+----+-----------+
I tried to implement a following query in order to get my required output but did not work:
SELECT ks,
STUFF (
(SELECT ', ' + t2.codes as [text()]
from table2 as t2 where t1.ks = t2.ks FOR XML PATH('')
),1,1,''
) as "codes"
from table1 t1
group by ks;
I get this table as result:
+----+------+
| ks | codes|
+----+------+
| 11 | aaaa |
+----+------+
| 11 | bbbb |
+----+------+
| 12 | cccc |
+----+------+
| 12 | dddd |
+----+------+
then this image below shows my required output:
required result
I did something wrong but I do not know what could be. Any chance someone help me? Thanks!
Try this. I think you posted the wrong output.
Create table #tbl (ks int , codes varchar(10))
Insert into #tbl values
(11 ,'aaaa'),
(12 ,'bbbb'),
(13 ,'cccc'),
(14 ,'dddd')
Create table #tbl2 (id int, ks int , codes varchar(10))
Insert into #tbl2 values
( 1 ,11 ,'aaaaa'),
( 2 ,11 ,'bbbbb'),
( 3 ,12 ,'aaaaa'),
( 3 ,13 ,'ccccc'),
( 4 ,12 ,'bbbbb')
with cte as
(Select t1.ks, t2.codes
from #tbl t1 join #tbl2 t2 on t1.ks = t2.ks)
Select ks, STUFF(
(SELECT ',' + codes FROM cte c1
where c1.ks = c2.ks FOR XML PATH ('')), 1, 1, ''
)
from cte c2
group by ks
Output:
ks
11 aaaaa,bbbbb
12 aaaaa,bbbbb
13 ccccc
I cannot say that I fully understand what is going on in your tables--especially given your output image appears to have no relation to your sample tables--but it looks like you want a comma-delimited list of sub-values from table2 that are associated with table1.
Here's a working example that I think addresses your need. You can use CROSS APPLY in these situations. Doing so allows you to return all values from table1 regardless of a matching record in table2.
DECLARE #table1 TABLE ( [ks] INT, [code] VARCHAR(10) );
DECLARE #table2 TABLE ( [id] INT, [ks] INT, [code] VARCHAR(10) );
-- populate table1 --
INSERT INTO #table1 (
[ks], [code]
)
VALUES
( 11, 'aaaa' )
, ( 12, 'bbbb' )
, ( 13, 'cccc' )
, ( 14, 'dddd' );
-- populate table two --
INSERT INTO #table2 (
[id], [ks], [code]
)
VALUES
( 1, 11, 'aaaaa' )
, ( 2, 11, 'bbbbb' )
, ( 3, 12, 'aaaaa' )
, ( 3, 13, 'ccccc' )
, ( 4, 12, 'bbbbb' );
SELECT
t1.ks, codes.codes
FROM #table1 t1
CROSS APPLY (
SELECT (
STUFF(
( SELECT ', ' + t2.code AS "text()" FROM #table2 t2 WHERE t2.ks = t1.ks FOR XML PATH ( '' ) )
, 1, 2, ''
)
) AS [codes]
) AS codes
ORDER BY
t1.ks;
Resulting Output:
ks codes
11 aaaaa, bbbbb
12 aaaaa, bbbbb
13 ccccc
14 NULL

SQL Query group by in case statement

I Have three tables like bellow
**tRole**
+--------+----------+-----------+
| RoleID | RoleCode | RoleTitle |
+--------+----------+-----------+
| 1 | Role1 | RT1 |
| 2 | Role2 | RT2 |
| 3 | Role3 | RT3 |
+--------+----------+-----------+
**tEmployee**
+-------+-------+
| EmpID | Name |
+-------+-------+
| 1 | Emp 1 |
| 2 | Emp 2 |
| 3 | Emp 3 |
+-------+-------+
**tEmployeeRole**
+-------+--------+
| EmpID | RoleID |
+-------+--------+
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 2 | 2 |
| 3 | 3 |
+-------+--------+
I want a output like below ,If a role mapped with only one employee then employee name will show other wise Multiple will show.
+--------+----------+-----------+----------+
| RoleID | RoleCode | RoleTitle | Employee |
+--------+----------+-----------+----------+
| 1 | Role1 | RT1 | Multiple |
| 2 | Role2 | RT2 | Multiple |
| 3 | Role3 | RT3 | Emp 3 |
+--------+----------+-----------+----------+
I write bellow query but when I group by emp.First_Name then the result is wrong
select cr.RoleCode,cr.RoleID,
case
when count(ear.RoleID)=1 then emp.First_Name
else 'M' end as 'AssignedTO'
from tRole as cr
left outer join tEmployeeRole as ear on cr.RoleID=ear.RoleID
left outer join tEmployee as emp on ear.EmployeeID=emp.EmployeeID
group by cr.RoleCode,crRoleID,emp.First_Name
Hello You can use this query for your solution :
you need to count with partition and use distinct data
DECLARE #tRole TABLE (
RoleID INT
,RoleCode VARCHAR(50)
,RoleTitle VARCHAR(50)
)
DECLARE #tEmployee TABLE (
EmpID INT
,EmpName VARCHAR(50)
)
DECLARE #tEmployeeRole TABLE ( EmpID INT, RoleID INT )
INSERT #tRole ( RoleID, RoleCode, RoleTitle )
SELECT 1, 'Role1', 'RT1'
UNION
SELECT 2, 'Role2', 'RT2'
UNION
SELECT 3, 'Role3', 'RT3'
INSERT #tEmployee ( EmpID, EmpName )
SELECT 1, 'Epm 1'
UNION
SELECT 2, 'Epm 2'
UNION
SELECT 3, 'Epm 3'
INSERT #tEmployeeRole ( EmpID, RoleID )
SELECT 1, 1
UNION
SELECT 1, 2
UNION
SELECT 2, 1
UNION
SELECT 2, 2
UNION
SELECT 3, 3
SELECT DISTINCT tRole.RoleID
, RoleCode
, RoleTitle
, CASE WHEN COUNT(tEmployeeRole.EmpID) OVER ( PARTITION BY tEmployee.EmpID ) = 1 THEN tEmployee.EmpName ELSE 'Multiple'END [Employee]
FROM #tEmployee tEmployee
LEFT OUTER JOIN #tEmployeeRole tEmployeeRole ON tEmployeeRole.EmpID = tEmployee.EmpID
LEFT OUTER JOIN #tRole tRole ON tRole.RoleID = tEmployeeRole.RoleID
You can Modify the answer from #Pratik to add a col that lists the employees
;with CTE as(
SELECT
DISTINCT tRole.RoleID
RoleCode
, RoleTitle
, CASE WHEN COUNT(tEmployeeRole.EmpID) OVER ( PARTITION BY tEmployee.EmpID ) = 1 THEN tEmployee.EmpName ELSE 'Multiple'END [Employee]
FROM #tEmployee tEmployee
LEFT OUTER JOIN #tEmployeeRole tEmployeeRole ON tEmployeeRole.EmpID = tEmployee.EmpID
LEFT OUTER JOIN #tRole tRole ON tRole.RoleID = tEmployeeRole.RoleID
)
select *
,stuff( (select ','+EmpName from #tEmployee IE inner join #tEmployeeRole IER on IE.EmpID = IER.EmpID where IER.RoleID = CTE.rolecode for xml PATH('') ),1,1,'') AS EMList
from CTE
This query might help you out . Make a try
Your Tables Data looks like
create TABLE #tRole (RoleID INT ,RoleCode VARCHAR(50) ,RoleTitle VARCHAR(50) )
create TABLE #tEmployee (EmpID INT ,EmpName VARCHAR(50) )
create TABLE #tEmployeeRole( EmpID INT, RoleID INT )
INSERT #tRole ( RoleID, RoleCode, RoleTitle )
SELECT 1, 'Role1', 'RT1'
UNION
SELECT 2, 'Role2', 'RT2'
UNION
SELECT 3, 'Role3', 'RT3'
INSERT #tEmployee ( EmpID, EmpName )
SELECT 1, 'Epm 1'
UNION
SELECT 2, 'Epm 2'
UNION
SELECT 3, 'Epm 3'
INSERT #tEmployeeRole ( EmpID, RoleID )
SELECT 1, 1
UNION
SELECT 1, 2
UNION
SELECT 2, 1
UNION
SELECT 2, 2
UNION
SELECT 3, 3
Desired Query
;with cte as
(
select tr.roleid,tr.rolecode,tr.roletitle,te.empname
,COUNT(ter.EmpID) OVER ( PARTITION BY ter.EmpID ) as emp_count
from #tEmployee te
inner join #tEmployeeRole tER on tER.empid=te.empid
inner join #tRole tr on tr.roleid=ter.roleid
)
select distinct RoleID,RoleCode,RoleTitle
,case when emp_count>1 then 'Multiple' else empname end as Employee
from cte

Need help in creating a view in sql with following data

the table I am working on contains data in following format
Name | A | B | C
----------------
abc | 1 | 0 | 1
xyz | 0 | 1 | 1
pqr | 0 | 0 | 1
I need to create a view a like this
Name | Type
abc | A
abc | C
xyz | B
xyz | C
pqr | C
Will using case and when be helpful?
like
case when A=1 then 'A'
when B=1 then 'B'
when C=1 then 'C'
else ''
end as type
Thanks in advance!
Sample Table :
DECLARE #Table1 TABLE
(Name varchar(3), A int, B int, C int)
;
INSERT INTO #Table1
(Name, A, B, C)
VALUES
('abc', 1, 0, 1),
('xyz', 0, 1, 1),
('pqr', 0, 0, 1)
;
Script
Select Name,[Type] from (
select Name,CASE WHEN VAL = 1 then COL ELSE NULL END Type,VAL from #Table1
CROSS APPLY(VALUES('A',A),('B',B),('C',C))CS(COL,VAL)
)T WHERE T.Type IS NOT NULL
You can use union all
select name,'A' from t where a = 1 union all
select name,'B' from t where b = 1 union all
select name,'C' from t where c = 1;
You can also group the where condition as:
select name, col
from
(select name, 'A' col, a val from t union all
select name, 'B', b from t union all
select name, 'C', c from t)
where val = 1;
We can use union all to select all the records from the table and insert into the view. The Below query would suffice your requirement. Thank you.
create or replace view test1 as (
select name,field from (
select name,'A' as field from test where a = 1 union all
select name,'B' as field from test where b = 1 union all
select name,'C' as field from test where c = 1));

How to subtract two rows from one another if they have they share a value in another column

I am currently working in a database with the following structure:
Var | Value | ID
--------------
A | 1 | 1
B | 2 | 1
C | 3 | 1
A | 2 | 2
B | 4 | 2
C | 6 | 2
What I am trying to achieve is to subtract the value of Var C from the other Var's (B and C) sharing the same ID as Var C. In this case the output would be:
Var | Value | ID
--------------
A | -2 | 1
B | -1 | 1
C | 3 | 1
A | -4 | 2
B | -2 | 2
C | 6 | 2
To be honest I have absolutely no idea how to start on achieving this. I am familiar with many other programming languages, but SQL is still a challenge with difficult/specific queries.
Do a self join:
select t1.var,
case when t1.var = 'C' then t1.value
else t1.value - t2.value
end as value,
t1.id
from tablename t1
join tablename t2 ON t1.id = t2.id
where t2.var = 'C'
Note that value is a reserved word in ANSI SQL, so it should be delimited as "Value".
You could pre-analyse the "C" Values and then use this to remove them?
DECLARE #Data TABLE (
[Var] VARCHAR(1),
Value INT,
ID INT);
INSERT INTO #Data SELECT 'A', 1, 1;
INSERT INTO #Data SELECT 'B', 2, 1;
INSERT INTO #Data SELECT 'C', 3, 1;
INSERT INTO #Data SELECT 'A', 2, 2;
INSERT INTO #Data SELECT 'B', 4, 2;
INSERT INTO #Data SELECT 'C', 6, 2;
WITH CValues AS (
SELECT
ID,
Value
FROM
#Data
WHERE
[Var] = 'C')
SELECT
d.[Var],
CASE WHEN d.[Var] != 'C' THEN d.Value - c.Value ELSE d.Value END AS Value,
d.ID
FROM
#Data d
LEFT JOIN CValues c ON c.ID = d.ID;
...but yes, a self-join is probably a better solution:
DECLARE #Data TABLE (
[Var] VARCHAR(1),
Value INT,
ID INT);
INSERT INTO #Data SELECT 'A', 1, 1;
INSERT INTO #Data SELECT 'B', 2, 1;
INSERT INTO #Data SELECT 'C', 3, 1;
INSERT INTO #Data SELECT 'A', 2, 2;
INSERT INTO #Data SELECT 'B', 4, 2;
INSERT INTO #Data SELECT 'C', 6, 2;
SELECT
d.[Var],
CASE WHEN d.[Var] != 'C' THEN d.Value - c.Value ELSE d.Value END AS Value,
d.ID
FROM
#Data d
LEFT JOIN #Data c ON c.[Var] = 'C' AND c.ID = d.ID;