problem in writing a sql query - sql

Getting a problem in writing an sql query.
two tables:
1st: created patient table 2nd: already created doc table
patientid patientname docid workstatus docid docname
1 aaa 2 10 1 ggg
2 bbb 2 20 2 hhh
3 ccc 1 10 3 iii
4 ddd 3 10
5 eee 3 20
6 fff 2 10
expected output:
docname workstatus(10) workstatus(20)
ggg 1 0
hhh 2 1
iii 1 1
can also use temporary tables between the queries
Thanks in advance

Try this
Full working example
declare #patient as table(
patientID int IDENTITY(1,1) NOT NULL,
patientName varchar(25),
docID int,
workstatus smallint
)
declare #doc as table(
docID int IDENTITY(1,1) NOT NULL,
docname varchar(25)
)
insert into #patient
select 'aaa', 2, 10
union all
select 'bbb', 2, 20
union all
select 'ccc', 1, 10
union all
select 'ddd', 3, 10
union all
select 'eee', 3, 20
union all
select 'fff', 2, 10
insert into #doc
select 'ggg'
union all
select 'hhh'
union all
select 'iii'
select docname,
SUM(case when t1.workstatus = 10 THEN 1 ELSE 0 END) as [workstatus(10)],
SUM(case when t1.workstatus = 20 THEN 1 ELSE 0 END) as [workstatus(20)]
from #patient t1
inner join #doc t2 on t1.docid=t2.docid
GROUP BY docname

Select d.docname,
SUM(case when c.workstatus = 10 THEN 1 ELSE 0 END) as [WorkStatus(10)],
SUM(case when c.workstatus = 20 THEN 1 ELSE 0 END) as [WorkStatus(20)]
from created_patient_table c
inner join already_created_doc_table d on c.docid=d.docid
group by d.docid,d.docname

Related

Showing History of changes from a History table

I have a History table, which is created by Insert and Update triggers. The History row contains the row as it was on the Insert/Update.
What I am being asked to do is show the changes for each user through time. So, below is what I have in the form of my History table, and then, I created a dummy expected results.
DECLARE #MyTable TABLE
(
id INT NOT NULL IDENTITY(1,1),
userId INT NOT NULL,
locationId INT NOT NULL,
roleId INT NOT NULL,
lastUpdateUserId INT NOT NULL,
lastUpdateDate DATETIME NOT NULL
)
INSERT INTO #MyTable
(userId, locationId, roleId, lastUpdateUserId, lastUpdateDate)
SELECT 1, 1000, 1, 7, GETDATE()+1 UNION
SELECT 2, 1100, 5, 9, GETDATE()+2 UNION
SELECT 2, 1110, 5, 6, GETDATE()+3 UNION
SELECT 1, 1100, 3, 6, GETDATE()+4 UNION
SELECT 4, 1500, 5, 8, GETDATE()+5 UNION
SELECT 7, 1000, 8, 9, GETDATE()+6 UNION
SELECT 7, 1100, 9, 9, GETDATE()+7 UNION
SELECT 1, 1000, 3, 7, GETDATE()+8 UNION
SELECT 9, 1100, 5, 2, GETDATE()+9 UNION
SELECT 9, 1100, 6, 5, GETDATE()+10
SELECT * FROM #MyTable ORDER BY Id
DECLARE #ExpectedResult TABLE
(
ChangeType CHAR(1), -- I=Insert, U=Update
UserId INT,
ChangeDate DATETIME,
ChangedByUser INT,
FieldName VARCHAR(20),
OldValue INT,
NewValue INT
)
INSERT INTO #ExpectedResult
(ChangeType, UserId, ChangeDate, ChangedByUser, FieldName, OldValue, NewValue)
SELECT 'I', 1, '2015-APR-30 09:56:28', 7, 'locationId', NULL, 1000 UNION -- Row1
SELECT 'I', 1, '2015-APR-30 09:56:28', 7, 'roleId', NULL, 1 UNION -- Row1
SELECT 'U', 1, '2015-APR-07 10:27:42', 7, 'roleId', 1, 3 UNION -- Row 2
SELECT 'U', 1, '2015-MAY-03 10:27:42', 6, 'locationId', 1000, 1100 UNION -- Row 3
SELECT 'I', 2, '2015-MAY-01 10:27:42', 9, 'roleId', NULL, 5 UNION -- Row5
SELECT 'I', 2, '2015-MAY-01 10:27:42', 9, 'locationId', NULL, 1100 -- Row5
SELECT * FROM #ExpectedResult
#MyTable has the data as it is at the moment. I am trying to transform that into #ExpectedResults. We're reporting on changes to roleId and locationId. On each change, it needs to have a separate line for each column. So, on insert, we have two lines (As we monitor changes to both fields). When one column is updated, it needs to be represented as one 'U' line. If both fields are updated in the same UPDATE statement, then that would result in two update rows in #Expected.
I started with a Cursor, but was hoping there would be a more efficient way to achieve this.
To get the roleID and locationID on separate rows you can use a simple UNION ALL.
And to combine the old and new values use the ROW_NUMBER() window function, like this:
;with t as(
select *,
ROW_NUMBER() OVER(partition by userid Order BY lastUpdateDate) rn
from #MyTable
),
a as (
select userId, 'locationId' as fieldname,
locationId as value, lastUpdateUserId, lastUpdateDate, rn
from t
UNION ALL
select userId, 'roleId' as fieldname,
roleId as value, lastUpdateUserId, lastUpdateDate, rn
from t
)
select CASE WHEN a2.userId IS NULL THEN 'I' ELSE 'U' END as ChangeType,
a1.userId, a1.lastUpdateDate, a1.lastUpdateUserId, a1.fieldname, a1.value as newValue, a2.value as oldvalue
FROM a a1 LEFT JOIN a a2
ON a1.userId = a2.userId and a1.fieldname = a2.fieldname
AND a1.rn = a2.rn+1
order by 2,3,5
The a1 alias in the query above contains the "new values", the a2 contains the "old values". When you use the real data you will also need to partition by the fieldname (and perhaps table name) and also to join by them
The result:
ChangeType userId lastUpdateDate lastUpdateUserId fieldname newValue oldvalue
---------- ----------- ----------------------- ---------------- ---------- ----------- -----------
I 1 2015-04-30 12:20:59.183 7 locationId 1000 NULL
I 1 2015-04-30 12:20:59.183 7 roleId 1 NULL
U 1 2015-05-03 12:20:59.183 6 locationId 1100 1000
U 1 2015-05-03 12:20:59.183 6 roleId 3 1
U 1 2015-05-07 12:20:59.183 7 locationId 1000 1100
U 1 2015-05-07 12:20:59.183 7 roleId 3 3
I 2 2015-05-01 12:20:59.183 9 locationId 1100 NULL
I 2 2015-05-01 12:20:59.183 9 roleId 5 NULL
U 2 2015-05-02 12:20:59.183 6 locationId 1110 1100
U 2 2015-05-02 12:20:59.183 6 roleId 5 5
I 4 2015-05-04 12:20:59.183 8 locationId 1500 NULL
I 4 2015-05-04 12:20:59.183 8 roleId 5 NULL
I 7 2015-05-05 12:20:59.183 9 locationId 1000 NULL
I 7 2015-05-05 12:20:59.183 9 roleId 8 NULL
U 7 2015-05-06 12:20:59.183 9 locationId 1100 1000
U 7 2015-05-06 12:20:59.183 9 roleId 9 8
I 9 2015-05-08 12:20:59.183 2 locationId 1100 NULL
I 9 2015-05-08 12:20:59.183 2 roleId 5 NULL
U 9 2015-05-09 12:20:59.183 5 locationId 1100 1100
U 9 2015-05-09 12:20:59.183 5 roleId 6 5
(20 row(s) affected)

sql server : count records

I have a tableA (ID int, Match varchar, tot int)
ID Match Tot
1 123
2 123
3 12
4 12
5 4
6 12
7 8
Now, I want to calculate Tot which is total number of match exists in the table. for example 123 occured twice, 12 exist thrice and so on. Also note that I want the count only at first match. here is the expected result.:
ID Match Tot
1 123 2
2 123
3 12 3
4 12
5 4 1
6 12
7 8 1
Another case:
ID Match Count Tot
1 123 2
2 123 1
3 12 10
4 12 10
5 4 3
6 12 5
7 8 7
Now I want to add the count for the same match. expected result:
ID Match Count Tot
1 123 2 3
2 123 1
3 12 10 25
4 12 10
5 4 3 3
6 12 5
7 8 7 7
Thanks
WITH tableA(ID, Match) AS
(
SELECT 1,123 UNION ALL
SELECT 2,123 UNION ALL
SELECT 3,12 UNION ALL
SELECT 4,12 UNION ALL
SELECT 5,4 UNION ALL
SELECT 6,12 UNION ALL
SELECT 7,8
)
SELECT *,
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY Match ORDER BY ID) = 1
THEN COUNT(*) OVER (PARTITION BY Match)
END AS Tot
FROM tableA
ORDER BY ID
SELECT match, COUNT(match ) as Tot
FROM tableA
GROUP BY match
Solution 1:
DECLARE #MyTable TABLE
(
ID INT PRIMARY KEY
,Match VARCHAR(10) NOT NULL
,Tot INT NULL
);
INSERT #MyTable(ID, Match)
SELECT 1, 123
UNION ALL
SELECT 2, 123
UNION ALL
SELECT 3, 12
UNION ALL
SELECT 4, 12
UNION ALL
SELECT 5, 4
UNION ALL
SELECT 6, 12
UNION ALL
SELECT 7, 8;
--SELECT
SELECT *
,CASE
WHEN ROW_NUMBER()OVER(PARTITION BY a.Match ORDER BY a.ID ASC)=1
THEN COUNT(*)OVER(PARTITION BY a.Match)
END TotCalculated
FROM #MyTable a;
--UPDATE
WITH MyCTE
AS
(
SELECT a.Tot
,CASE
WHEN ROW_NUMBER()OVER(PARTITION BY a.Match ORDER BY a.ID ASC)=1
THEN COUNT(*)OVER(PARTITION BY a.Match)
END TotCalculated
FROM #MyTable a
)
UPDATE MyCTE
SET Tot = TotCalculated;
SELECT *
FROM #MyTable;
Solution 2:
UPDATE #MyTable
SET Tot = NULL;
SELECT x.ID, y.Num
FROM
(
SELECT b.Match, MIN(b.ID) ID
FROM #MyTable b
GROUP BY b.Match
) x INNER JOIN
(
SELECT a.Match, COUNT(*) AS Num
FROM #MyTable a
GROUP BY a.Match
) y ON x.Match = y.Match
ORDER BY x.ID
UPDATE #MyTable
SET Tot = t.Num
FROM #MyTable z
INNER JOIN
(
SELECT x.ID, y.Num
FROM
(
SELECT b.Match, MIN(b.ID) ID
FROM #MyTable b
GROUP BY b.Match
) x INNER JOIN
(
SELECT a.Match, COUNT(*) AS Num
FROM #MyTable a
GROUP BY a.Match
) y ON x.Match = y.Match
) t ON z.ID = t.ID;
SELECT *
FROM #MyTable;

How to Mapping Columns in a Self-Join table!

I have a parent/child table and want to update Its PK and FK to new values. the problem is that oldParent Ids Should Sync with new ones matching with Old Ids. so:
I have this data as a temp table:
OldID | OldParentID | NewID | NewParentID
1 0 10 NULL
2 0 11 NULL
3 2 13 NULL
4 3 14 NULL
and I Need Update NewParentID as Follows:
OldID | OldParentID | NewID | NewParentID
1 0 10 0
2 0 11 0
3 2 13 11
4 3 14 13
declare #T table
(
OldID int,
OldParentID int,
NewID int,
NewParentID int
)
insert into #T
select 1, 0, 10, null union all
select 2, 0, 11, null union all
select 3, 2, 13, null union all
select 4, 3, 14, null
update T1
set T1.NewParentID = coalesce(T2.NewID, 0)
from #T as T1
left outer join #T as T2
on T1.OldParentID = T2.OldID

Projection/Replication in SQL Query?

My SQL is a bit rusty -- is there a SQL way to project an input table that looks something like this:
Name SlotValue Slots
---- --------- -----
ABC 3 1
ABC 4 2
ABC 6 5
Into a 'projected' result table that looks like this:
Name SlotSum Slot
---- ------- ----
ABC 13 1
ABC 10 2
ABC 6 3
ABC 6 4
ABC 6 5
In other words, the result set should contain a number of rows equal to MAX(Slots), enumerated (Slot) from 1 to MAX(Slots), and Sum for each of these 'slots' should reflect the sum of the SlotValues projected out to the 'Slots' position. for the pathological case:
Name SlotValue Slots
---- --------- -----
ABC 4 3
we should get:
Name SlotSum Slot
---- ------- ----
ABC 4 1
ABC 4 2
ABC 4 3
The summation logic is pretty straightforward -- project each SlotValue out to the number of Slots:
SlotValue SlotValue SlotValue Slot Sum
--------- --------- --------- ---- ---
3 4 6 1 13 (3+4+6)
0 4 6 2 10 (0+4+6)
0 0 6 3 6 (0+0+6)
0 0 6 4 6 (0+0+6)
0 0 6 5 6 (0+0+6)
UPDATE: In the end I used a variant of LOCALGHOST's approach in a stored proc. I was hoping there might be a way to do this without a loop.
I'm not sure you'll be able to do this in a view. You'd have to use a procedure.
You could make ProjectedTable a temporary/variable table in the procedure as well. I'd be really interested to see how you'd get this into a view because you need to dynamically generate a range of numbers.
declare #maxSlot int
set #maxSlot = select max(slots) from SlotTable
truncate ProjectedTable
while #i > 0
begin
insert into ProjectedTable (
SlotSum
,Slot
) values (
(select sum(slotValue) from SlotTable where slots >= #maxSlot)
,#maxSlot
)
set #maxSlot = #maxSlot - 1
end
select SlotSum, Slot from ProjectedTable
Here you go. This will do up to 100 slots in its current form. You can use your imagination to accommodate more.
DECLARE #SLOT TABLE
(
SlotName varchar(25) NOT NULL,
SlotValue int NOT NULL,
Slot int NOT NULL
)
INSERT INTO #SLOT (SlotName, SlotValue, Slot)
SELECT 'ABC', 3, 1
UNION
SELECT 'ABC', 4, 2
UNION
SELECT 'ABC', 6, 5
SELECT
CASE
WHEN SLOT.SlotName IS NOT NULL THEN SLOT.SlotName
ELSE
COALESCE(
(SELECT TOP 1 SL.SlotName FROM #SLOT AS SL WHERE SL.Slot < SLOT_PROJECT.Slot ORDER BY SL.Slot DESC),
(SELECT TOP 1 SL.SlotName FROM #SLOT AS SL WHERE SL.Slot > SLOT_PROJECT.Slot ORDER BY SL.Slot ASC)
)
END AS SlotName,
(
SELECT
SUM(SLOT10.SlotValue)
FROM
#SLOT AS SLOT10
WHERE
SLOT10.Slot >= SLOT_PROJECT.Slot
) AS SlotSum,
SLOT_PROJECT.Slot
FROM
(
SELECT
(TENS.Seq + ONES.Seq) AS Slot
FROM
(
SELECT 1 AS Seq
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
UNION ALL
SELECT 5
UNION ALL
SELECT 6
UNION ALL
SELECT 7
UNION ALL
SELECT 8
UNION ALL
SELECT 9
) AS ONES
CROSS JOIN
(
SELECT 0 AS Seq
UNION ALL
SELECT 10
UNION ALL
SELECT 20
UNION ALL
SELECT 30
UNION ALL
SELECT 40
UNION ALL
SELECT 50
UNION ALL
SELECT 60
UNION ALL
SELECT 70
UNION ALL
SELECT 80
UNION ALL
SELECT 90
) AS TENS
WHERE
(TENS.Seq + ONES.Seq) <= (SELECT MAX(Slot) FROM #SLOT)
) AS SLOT_PROJECT
LEFT JOIN #SLOT AS SLOT ON
SLOT.Slot = SLOT_PROJECT.Slot

Need sql query for matching with three values

I have a table like below
CAccountID CID NetworkID
1 1 1
2 1 2
3 2 1
4 2 2
5 2 3
6 3 1
7 3 2
8 3 3
9 4 1
10 4 2
I need a query to select all CID having all 3 NetworkID(1,2,3) and don't need to display only 1 and 2 NetworkID.
Output should be like below,
CAccountID CID NetworkID
3 2 1
4 2 2
5 2 3
6 3 1
7 3 2
8 3 3
You can use GROUP BY with JOIN :
select t.*
from table t inner join
( select cid
from table
where NetworkID in (1,2,3)
group by cid
having count(distinct NetworkID) = 3
) tt
on tt.cid = t.cid;
Try this:
select * from my_table t
where exists(select 1 from my_table
where CID = t.CID and NetworkID in (1,2,3)
group by CID
having count(*) = 3)
Try this:
select * from <<tablename>> where cid in(select cid from <<tablename>> group by cid having count(*)=3).
Here the subquery will return you all thouse cid which have 3 rows in your table.
Or if you have more network ids then use of INTERSECT operator can be helpful:
select * from <<tablename>> where cid in (
select cid from <<tablename>> where NetworkID=1
INTERSECT
select cid from <<tablename>> where NetworkID=2
INTERSECT
select cid from <<tablename>> where NetworkID=3
);
INTERSECT operator basically returns all the rows common in the queries. Thus, your data unpredicatbility can be handled in this way
Try xml path.
SELECT *
FROM Table_Name B
WHERE (SELECT [text()] = A.Network FROM Table_Name A WHERE A.CID = B.CID
ORDER BY CID, CAAccount FOR XML PATH('')) = 123
CTE Demo:
; WITH CTE(CAAccount, CID, Network) AS
(
SELECT 1 , 1, 1 UNION ALL
SELECT 2 , 1, 2 UNION ALL
SELECT 3 , 2, 1 UNION ALL
SELECT 4 , 2, 2 UNION ALL
SELECT 5 , 2, 3 UNION ALL
SELECT 6 , 3, 1 UNION ALL
SELECT 7 , 3, 2 UNION ALL
SELECT 8 , 3, 3 UNION ALL
SELECT 9 , 4, 1 UNION ALL
SELECT 10, 4, 2
) SELECT *
FROM CTE B
WHERE (SELECT [text()] = A.Network FROM CTE A WHERE A.CID = B.CID ORDER BY CID, CAAccount FOR XML PATH('')) = 123
Output:
CAAccount CID Network
3 2 1
4 2 2
5 2 3
6 3 1
7 3 2
8 3 3