Need to help query in SQL Server - sql-server

I have a question about SQL Server.
Table: patient
pn | hospid | doj
------------------------
1 | 10 | 2015-10-14
1 | 10 | 2015-05-14
1 | 10 | 2015-08-12
2nd table: patientrefs
sdate | edate | codes | descripton | pn | hospid
-------------------------------------------------------------
2015-01-01 | 2015-09-30 | 500 | active | 1 | 10
2015-01-01 | 2015-09-30 | 501 | inactive | 1 | 10
2015-10-01 | 2016-03-31 | 500 | activestill | 1 | 10
2015-10-01 | 2016-03-31 | 501 | inactive | 1 | 10
2013-03-09 | 2013-09-12 | 300 | inactive | 1 | 10
Both table common columns pn + hospid and patient tables related dos between sdate and edate of patientrefs table.
And in patientrefs table descritpton=inactive and date between condition satisfy then codes we consider inactivecodes
In patientrefs table descritpton<>inactive and date between condition satisfy then codes we consider activecodes
Based on this above table I want output like this:
pn|hospid|doj |inactivecodes| activecodes
------------------------------------------------
1 |10 |2015-05-14 | 501 | 500
1 |10 |2015-08-12 | 501 | 500
1 |10 |2015-10-14 | 501 | 500
I tried like this:
select
a.pn, a.hospid, a.doj,
case when b.descripton <> 'inactive' then b.codes end activecodes,
case when b.descripton = 'inactive' then b.codes end inactivecodes
from
patient a
left join
patientrefs b on a.pn = b.pn and a.hospid = b.hospid
and a.doj between b.sdate and b.edate
But that query is not returning the expected result.
And I tried another way
select
a.pn, a.hospid, a.doj, b.codes as inactivecodes
from
patient a
left join
patientrefs b on a.pn = b.pn and a.hospid = b.hospid
and a.doj between b.sdate and b.edate
where
b.descripton = 'inactive'
select
a.pn, a.hospid, a.doj, b.codes as activecode
from
patient a
left
patientrefs b on a.pn = b.pn and a.hospid = b.hospid
and a.doj between b.sdate and b.edate
where
b.descripton <> 'inactive'
Here the individual queries return the expected result, but I need active and inactivecodes in the above expected output format.
Please tell me how to write query to get my expected result in SQL Server

You can do this using conditional aggregation:
SELECT
p.pn,
p.hospid,
p.doj,
inactivecodes = MAX(CASE WHEN pr.description = 'inactive' THEN pr.codes END),
activecodes = MAX(CASE WHEN pr.description = 'active' THEN pr.codes END)
FROM patient p
LEFT JOIN patientrefs pr
ON p.pn = pr.pn
AND p.hospid = pr.hospid
AND p.doj BETWEEN pr.sdate AND pr.edate
GROUP BY
p.pn, p.hospid, p.doj

Related

How to get the top row from a SQL Server record set query and other constraint

I have two SQL Server tables as below:
Event
+------------+----------------------------+-------------+------------+-----------------------------+
| Id | EventTypeId | PersonId | UCNumber | Name |DateEvent
+------------+----------------------------+-------------+------------+-----------------------------+
| 2307 | 3 | 2189 | 004947 | Migrated | 1900-01-01 00:00:00.6780000 |
| 2308 | 15 | 2189 | 004947 | Birthday | 2020-09-18 16:48:32.6870000 |
| 3400 | 15 | 2190 | 006857 | Birthday | 1900-01-01 00:00:00.0000000 |
| 3401 | 2 | 2190 | 006857 | Migrated | 2016-03-12 00:00:00.0000000 |
Person
+------------+----------------+-------------------+-----------+-------------------------------+
| Id | UCNumber | Name |LastName | AnotherDate |
+------------+----------------+-------------------+-----------+-------------------------------+
| 2189 | 004947 | John | Smith | 1900-01-01 00:00:00.0000000 |
| 2190 | 006857 | Alice | Timo | 2020-02-20 00:00:00.0000000 |
I need to get retrieved the top row (latest in time) based on the Event's Id. (The higher the Id, the more recent the Event) and it should be a 15 as EventTypeId.
I tried this:
Select P.Id, P.UCNUMBER, P.AnotherDate from
db.dbo.Person P
Inner join db.dbo.Event L on L.PersonId = P.Id
where P.Id in (
SELECT TOP (1) PersonId
FROM
db.dbo.Event
where PersonId = P.Id --and EventTypeID = 15
ORDER BY
Id DESC)
and EventTypeId = 15
but it does not work properly. I posted here just samples from the 2 tables. Generally the query takes also other events which are not latest ones (as higher Id). Something is missing in it.
In this case, for instance, it should return only 1 row:
2189 004947 1900-01-01 00:00:00.0000000
Sounds like you just want ORDER BY and TOP 1.
SELECT TOP 1
p.id,
p.ucnumber,
p.anotherdate
FROM event e
LEFT JOIN person p
ON p.id = e.personid
WHERE e.eventtypeid = 15
ORDER BY e.dateevent DESC;
If you want all ties in case there are more events on the same latest time you can replace TOP 1 with TOP 1 WITH TIES.

How to find difference between two tables in MSSQL

I have got two tables 'Customer'.
The first one:
ID | UserID | Date
1. | 1 | 2018-05-01
2. | 1 | 2018-05-02
The second one:
ID | UserID | Date
1. | 1 | 2018-05-01
2. | 1 | 2018-05-02
3. | 1 | 2018-05-03
So, as you can see in the second table, there is one row more.
I have written so far this code:
;with cte_table1 as (
select UserID, count(id) cnt from db1.Customer group by UserID
),
cte_table2 as (
select UserID, count(id) cnt from db2.Customer group by UserID
)
select * from cte_table1 t1
join cte_table2 t2 on t2.UserID = t1.UserID
where t1.cnt <> t2.cnt
and this gives me expected result:
UserID | cnt | UserID | cnt
1 | 2 | 1 | 3
And so far, everything is fine. The thing is, these two tables have many rows and I'd like to have result with dates, where cnt does not match.
In other words, I'd like to have something like this:
UserID | cnt | Date | UserID | cnt | Date
1 | 2 | 2018-05-01 | 1 | 3 | 2018-05-01
1 | 2 | 2018-05-02 | 1 | 3 | 2018-05-01
1 | 2 | NULL | 1 | 3 | 2018-05-03
The best soulution would be resultset where both cte's are joined to give this:
UserID | cnt | Date | UserID | cnt | Date
1 | 2 | 2018-05-01 | 1 | 3 | 2018-05-01
1 | 2 | 2018-05-02 | 1 | 3 | 2018-05-01
1 | 2 | NULL | 1 | 3 | 2018-05-03
1 | 2 | 2018-05-30 | 1 | 3 | NULL
You should do a FULL OUTER JOIN query like below
Select
C1.UserID,
C1.cnt,
C1.Date,
C2.UserID,
C2.cnt,
C2.Date
from
db1.Customer C1
FULL OUTER JOIN
db2.Customer C2
on C1.UserId=C2.UserId and C1.date=C2.Date

Get all categories with number of associated records with where clause

So I have two tables:
Categories
-------------------
| Id | Name |
-------------------
| 1 | Category1 |
-------------------
| 2 | Category2 |
-------------------
| 3 | Category3 |
-------------------
Products
--------------------------------------------
| Id | CategoryId | Name | CreatedDate |
--------------------------------------------
| 1 | 1 | Product1 | 2017-05-05 |
--------------------------------------------
| 1 | 1 | Product2 | 2017-05-06 |
--------------------------------------------
| 2 | 2 | Product3 | 2017-12-21 |
--------------------------------------------
I need a query to select all categories along with the number of products for each for a specific time range in which those products were created (CreatedDate).
What I currently have is this:
SELECT c.[Name], COUNT(p.[Id]) AS ProductCount
FROM Categories AS c
LEFT JOIN Products AS p ON p.[CategoryId] = c.[Id]
WHERE p.[CreatedDate] BETWEEN '2017-05-01' AND '2017-06-01'
GROUP BY c.[Name]
My issue is that I'm not seeing Category2 and Category3 in the results set because they don't pass the WHERE clause. I want to see all categories in the results set.
Put the where condition in the left join clause
SELECT c.[Name], COUNT(p.[Id]) AS ProductCount
FROM Categories AS c
LEFT JOIN Products AS p ON p.[CategoryId] = c.[Id]
AND p.[CreatedDate] BETWEEN '2017-05-01' AND '2017-06-01'
GROUP BY c.[Name]
This way it is applied to the join only and not to the complete result set.

SQL Server join query optimization with group by

Just for knowledge, I want to know that, can the below given query be achieve by any other feasible way like using group by.
SELECT
GROUPMAS.GRPCODE, GROUPMAS.GRPNAME,
GRPDTLS.ACCODE, GRPDTLS.ACNAME, GRPDTLS.DOA "ADMISSION DATE",
LOANMAST.LOANCODE, LOANMAST.VCHDATE "LOAN SANCTION DATE",
LOANMAST.LANAMT,
(SELECT SUM(RECPDTLS.INSTAMT)
FROM RECPDTLS
WHERE LOANCODE = LOANMAST.LOANCODE
AND RECPDTLS.VCHDATE <= '2009-03-31') AS REPAYMENT,
(SELECT SUM(RECPDTLS.INTAMT)
FROM RECPDTLS
WHERE LOANCODE = LOANMAST.LOANCODE
AND RECPDTLS.VCHDATE <= '2009-03-31') AS INTREST,
(SELECT MAX(RECPDTLS.VCHDATE)
FROM RECPDTLS
WHERE LOANCODE = LOANMAST.LOANCODE
AND RECPDTLS.VCHDATE <= '2009-03-31') AS "LAST PAYMENT ON"
FROM
GROUPMAS
JOIN
GRPDTLS ON (GROUPMAS.GRPCODE = GRPDTLS.GRPCODE AND GRPDTLS.DOA <= '2009-03-31')
JOIN
LOANMAST ON (GRPDTLS.GRPCODE = LOANMAST.GRPCODE AND GRPDTLS.ACCODE = LOANMAST.ACCODE AND LOANMAST.VCHDATE <= '2009-03-31')
Table GROUPMAS structure
GRPCODE | GRPNAME
--------| -------
1 | A
2 | B
Table GRPDTLS structure
GRPCODE | ACCODE | ACNAME | DOA
--------|--------|--------|-----
1 | 1 | name1A | 2007-07-05
1 | 2 | name2A | 2008-07-05
2 | 1 | name1B | 2007-07-06
2 | 2 | name2B | 2007-07-05
Table LOANMAST structure
LOANCODE | GRPCODE | ACCODE | VCHDATE | LANAMT
---------|---------|--------|--------- |--------
1 | 1 | 2 |2009-01-06|2000
2 | 2 | 1 |2008-09-06|5000
Table RECPDTLS structure
TXNNO | LOANCODE | INSTAMT | INTAMT | VCHDATE
------|----------|---------|--------|---------
1 | 1 | 200 | 0 | 2009-02-06
2 | 1 | 200 | 10 | 2009-03-06
3 | 2 | 500 | 0 | 2008-10-06
4 | 2 | 1500 | 50 | 2009-03-28
5 | 2 | 500 | 0 | 2010-03-28
It will output something like this
GRPCODE | GRPNAME | ACCODE | ACNAME | ADMISSION DATE | LOANCODE | LOAN SANCTION DATE | LANAMT | REPAYMENT | INTREST | LAST PAYMENT ON
--------| --------| -------| ------ | ---------------| -------- | ------------------ | -------| ----------| ------- | --------------
1 | A | 2 | name2A | 2008-07-05 | 1 |2009-01-06 | 2000 | 400 | 10 | 2009-03-06
2 | B | 1 | name1B | 2007-07-06 | 2 |2008-09-06 | 5000 | 2000 | 50 | 2009-03-28
Thanks for the help.
You can replace the sub queries in your select statement with LEFT OUTER OR INNER JOIN depending on requirements. If all LOANCODE records will have matching RECPDTLS records then use INNER JOIN else use LEFT OUTER JOIN. Keep your aggregate functions the same.
...
Repayment=SUM(RECPDTLS.INSTAMT),
Interest=SUM(RECPDTLS.INTAMT),
LastPaymentOn=MAX(RECPDTLS.VCHDATE)
...
LEFT OUTER/INNER JOIN RECPDTLS ON RECPDTLS.LOANCODE = LOANMAST.LOANCODE AND Repayment.VCHDATE <= #HighDate
...
GROUP BY
GROUPMAS.GRPCODE, GROUPMAS.GRPNAME,
GRPDTLS.ACCODE, GRPDTLS.ACNAME, GRPDTLS.DOA,
LOANMAST.LOANCODE, LOANMAST.VCHDATE,
LOANMAST.LANAMT
You will need to run the query analyzer to see the efficiency gain between the old and the new queries.
NOTE : As I said above, be sure to use LET OUTER JOIN if the LOANCODE is not required to have a RECPDTLS as an INNER JOIN will only return matches in both tables.
You can use CTE to simplify the request :
;WITH LOANMASTAGG AS
(
SELECT SUM(r.INSTAMT) REPAYMENT, SUM(r.INTAMT) INTREST, MAX(r.VCHDATE) [LAST PAYMENT ON], l.LOANCODE, l.VCHDATE, l.LANAMT, l.ACCODE, l.GRPCODE
FROM #RECPDTLS r
INNER JOIN #LOANMAST l ON r.LOANCODE = l.LOANCODE
WHERE l.VCHDATE <= '2009-03-31'
GROUP BY l.LOANCODE, l.VCHDATE, l.LANAMT, l.ACCODE, l.GRPCODE
)
SELECT
g.GRPCODE,
g.GRPNAME,
gl.ACCODE,
gl.ACNAME,
gl.DOA "ADMISSION DATE",
la.LOANCODE,
la.VCHDATE "LOAN SANCTION DATE",
la.LANAMT,
la.REPAYMENT AS REPAYMENT,
la.INTREST AS INTREST,
la.[LAST PAYMENT ON] "LAST PAYMENT ON"
FROM LOANMASTAGG la
INNER JOIN #GRPDTLS gl ON gl.GRPCODE = la.GRPCODE AND gl.ACCODE = la.ACCODE
INNER JOIN #GROUPMAS g ON (g.GRPCODE = gl.GRPCODE)
WHERE gl.DOA <= '2009-03-31'

Exclude Secondary ID Records from Original SELECT

I'm relatively new to SQL and am running into a lot of issues trying to figure this one out. I've tried using a LEFT JOIN, and dabbled in using functions to get this to work but to no avail.
For every UserID, if there is a NULL value, I need to remove all records of the Product ID for that UserID from my SELECT.
I am using SQL Server 2014.
Example Table
+--------------+-------------+---------------+
| UserID | ProductID | DateTermed |
+--------------+-------------+---------------+
| 578 | 2 | 1/7/2017 |
| 578 | 2 | 1/7/2017 |
| 578 | 1 | 1/15/2017 |
| 578 | 1 | NULL |
| 649 | 1 | 1/9/2017 |
| 649 | 2 | 1/11/2017 |
+--------------+-------------+---------------+
Desired Output
+--------------+-------------+---------------+
| UserID | ProductID | DateTermed |
+--------------+-------------+---------------+
| 578 | 2 | 1/7/2017 |
| 578 | 2 | 1/7/2017 |
| 649 | 1 | 1/9/2017 |
| 649 | 2 | 1/11/2017 |
+--------------+-------------+---------------+
Try the following:
SELECT a.userid, a.productid, a.datetermed
FROM yourtable a
LEFT OUTER JOIN (SELECT userid, productid, datetermed FROM yourtable WHERE
datetermed is null) b
on a.userid = b.userid and a.productid = b.productid
WHERE b.userid is not null
This will left outer join all records with a null date to their corresponding UserID and ProductID records. If you only take records that don't have an associated UserID and ProductID in the joined table, you should only be left with records that don't have a null date.
You can use this WHERE condition:
SELECT
UserID,ProducID,DateTermed
FROM
[YourTableName]
WHERE
(CONVERT(VARCHAR,UserId)+
CONVERT(VARCHAR,ProductID) NOT IN (
select CONVERT(VARCHAR,UserId)+ CONVERT(VARCHAR,ProductID)
from
[YourTableName]
where DateTermed is null)
)
When you concatenate the UserId and the ProductId get a unique value for each pair, then you can use them as a "key" to exclude the "pairs" that have the null value in the DateTermed field.
Hope this help.

Resources