Lookup a value on another table with no relationship - sql

In TSQL, how do we select a value from a table with no relationship to the other.
I'm trying to select what rank an ID has, by looking up their score in another table. I was going to join and then use a 'where Score between From and TO' but, I had no luck joining.
Table_A
ID
Score
A
67
B
569
C
123
Table_B
From
To
Rank
1
99
Top100
100
499
Top500
500
999
Top1000
Expected query result:
ID
Rank
A
Top100
B
Top1000
C
Top500
I started with
Select ID From Table_A
Inner Join
I got lost here because there is no relationship
I could get the result using a scalar function, but in terms of performance, where Table_A has over 500k rows, it seemed a little sluggish because Table_B not only holds rank, but has other columns I need for the query.
For example:
Table_B
From
To
Rank
Level
Color
Category
1
99
Top100
Gold
Green
1
100
499
Top500
Silver
Yellow
5
500
999
Top1000
Bronze
Red
100
Basically, if I can be shown how to query at least the rank, I can get the other columns as well.

You can use BETWEEN as the JOIN condition, like so:
CREATE TABLE #Table_A
(
ID VARCHAR(255),
Score INT
)
;
INSERT #Table_A ([ID], [Score]) VALUES
('A', 67),
('B', 569),
('C', 123);
CREATE TABLE #Table_B
(
[From] INT,
[To] INT,
[Rank] VARCHAR(255)
)
INSERT #Table_B ([From], [To], [Rank]) VALUES
( 1, 99, 'Top100'), (100, 499, 'Top500'), (500, 999, 'Top1000');
-- Query here
SELECT A.[ID], B.[Rank]
FROM #Table_A A
INNER JOIN #Table_B B ON A.Score BETWEEN B.[From] AND B.[To]

You could also use CROSS APPLY to get your favorite result
;WITH Table_A AS
(
SELECT 'A' ID, 67 Score
Union
SELECT 'B' ID, 569 Score
Union
SELECT 'C' ID, 123 Score
),
Table_B AS
(
SELECT 1 [From], 99 [To], 'Top100' [Rank]
Union
SELECT 100 [From], 499 [To], 'Top500' [Rank]
Union
SELECT 500 [From], 999 [To], 'Top1000' [Rank]
)
SELECT Table_A.[ID], Table_B.[Rank]
FROM Table_A
CROSS APPLY Table_B
WHERE Table_A.Score
BETWEEN Table_B.[From] AND Table_B.[To]
GO

Related

MariaDB concatenate 2 tables with same number of rows

Say I have 2 tables with exactly SAME number of rows, but no other obvious relations:
tableA
ID
items
1
banana
2
orange
tableB
itemID
volume
5550
50
5551
70
Can I join these 2 tables horizontally, to form 1 table like the following?
ID
items
itemID
volume
1
banana
5550
50
2
orange
5551
70
If you have 2 tables with exactly SAME number of rows, but no other obvious relations and on both tables , respectively ID and itemID defines the uniqueness of the rows you can apply MySQL ROW_NUMBER Function and join on the row_number, the order by clause is important.
Try:
SELECT tbla.ID, tbla.Items, tblb.ItemId, tblb.volume
FROM (
SELECT ID, Items, row_number() over( order by ID desc )row_numA
FROM TableA
)tbla
INNER join
( SELECT ItemId,volume,row_number() over(order by ItemId desc)row_numB
FROM TableB
) tblb ON tbla.row_numA=tblb.row_numB
order by tbla.ID asc;
https://dbfiddle.uk/?rdbms=mariadb_10.6&fiddle=15d13d29a84a55c4d029115c87eebe8f
try this
create table TableA(ID INT, Items varchar(20));
create table TableB(ItemId INT, volume varchar(20));
insert into TableA(Id, items) values (1, 'banana'), (2, 'orange');
insert into TableB(ItemId, volume) values (5550, '50'), (5551, '70');
SELECT A.ID, A.Items, B.ItemId, B.volume
FROM
(
SELECT ID, Items, rownum()R
FROM TableA
)A INNER join
(
SELECT ItemId,volume,rownum()R
FROM TableB
)B ON A.R=B.R

SQL Server : SELECT ID having only a single condition

I have a patients table with details such as conditions that the patient has. from the below table I want to select Patients, Claims which have ONLY a single condition - 'Hypertension'. Example Patient B is the expected output. Patient A will not be selected because he claimed for multiple conditions.
+----+---------+--------------+
| ID | ClaimID | Condition |
+----+---------+--------------+
| A | 14234 | Hypertension |
| A | 14234 | Diabetes |
| A | 63947 | Diabetes |
| B | 23853 | Hypertension |
+----+---------+--------------+
I tried using the NOT IN condition as below but doesn't seem to help
SELECT ID, ClaimID, Condition
FROM myTable
WHERE Condition IN ('Hypertension')
AND Condition NOT IN ('Diabetes')
One method uses not exists:
select t.*
from mytable t
where t.condition = 'Hypertension' and
not exists (select 1
from mytable t2
where t2.id = t.id and t2.condition <> t.condition
);
Or you can do it like this:
select
id,
claim_id,
condition
from
patient
where
id in
(
select
id
from
patient
group by
id having count (distinct condition) = 1
);
Result:
id claim_id condition
-- ----------- ----------------
B 23853 Hypertension
(1 rows affected)
Setup:
create table patient
(
id varchar(1),
claim_id int,
condition varchar(16)
);
insert into patient (id, claim_id, condition) values ('A', 14234, 'Hypertension');
insert into patient (id, claim_id, condition) values ('A', 14234, 'Diabetes');
insert into patient (id, claim_id, condition) values ('A', 63947, 'Diabetes');
insert into patient (id, claim_id, condition) values ('B', 23853, 'Hypertension');
You can do this with a CTE.
I set up this CTE with two parameters, one being the Condition you seek, and the other being the max number of combined conditions to find (in your case 1).
DECLARE #myTable TABLE (Id VARCHAR(1), ClaimID INT, Condition VARCHAR(100))
INSERT INTO #myTable (Id, ClaimID, Condition)
SELECT 'A',14234,'Hypertension' UNION ALL
SELECT 'A',14234,'Diabetes' UNION ALL
SELECT 'A',63947,'Diabetes' UNION ALL
SELECT 'B',23853,'Hypertension'
DECLARE #Condition VARCHAR(100)
DECLARE #MaxConditions TINYINT
SET #Condition='Hypertension'
SET #MaxConditions=1
; WITH CTE AS
(
SELECT *, COUNT(2) OVER(PARTITION BY ClaimID) AS CN
FROM #myTable T1
WHERE EXISTS (SELECT 1 FROM #myTable T2 WHERE T1.ClaimID=T2.ClaimID AND T2.Condition=#Condition)
)
SELECT *
FROM CTE
WHERE CN<=#MaxConditions
If you don't care about the fluff, and just want all ClaimID's with just ONE condition regardless of which condition it is use this.
DECLARE #myTable TABLE (Id VARCHAR(1), ClaimID INT, Condition VARCHAR(100))
INSERT INTO #myTable (Id, ClaimID, Condition)
SELECT 'A',14234,'Hypertension' UNION ALL
SELECT 'A',14234,'Diabetes' UNION ALL
SELECT 'A',63947,'Diabetes' UNION ALL
SELECT 'B',23853,'Hypertension'
DECLARE #MaxConditions TINYINT
SET #MaxConditions=1
; WITH CTE AS
(
SELECT *, COUNT(2) OVER(PARTITION BY ClaimID) AS CN
FROM #myTable T1
)
SELECT *
FROM CTE
WHERE CN<=#MaxConditions
Here is one method using Having clause
SELECT t.*
FROM mytable t
WHERE EXISTS (SELECT 1
FROM mytable t2
WHERE t2.id = t.id
HAVING Count(CASE WHEN condition = 'Hypertension' THEN 1 END) > 0
AND Count(CASE WHEN condition != 'Hypertension' THEN 1 END) = 0)
And yet a couple of other ways to do this:
declare #TableA table(Id char,
ClaimId int,
Condition varchar(250));
insert into #TableA (id, claimid, condition)
values ('A', 14234, 'Hypertension'),
('A', 14234, 'Diabetes'),
('A', 63947, 'Diabetes'),
('B', 23853, 'Hypertension')
select id, claimid, condition
from #TableA a
where not exists(select id
from #TableA b
where a.id = b.id
group by b.id
having count(b.id) > 1)
OR
;with cte as
(
select id, claimid, condition
from #TableA
)
,
cte2 as
(
Select id, count(Id) as counts
from cte
group by id
having count(id) < 2
)
Select cte.id, claimid, condition
From cte
inner join
cte2
on cte.id = cte2.id
I decided to revise my answer into an appropriate one.
A simple solution to your question is to count the rows instead of the ID values (since it's not an integer).
Here is a simple introduction:
SELECT
ID
FROM
#PatientTable
GROUP BY
ID
HAVING
ID = ID AND COUNT(*) = 1
This will Return the ID B
+----+
| ID |
+----+
| B |
+----+
Surely, this is not enough, as you may work with a large data and need more filtering.
So, we will go and use it as a sub-query.
Using it as a sub-query it's simple :
SELECT
ID,
ClaimID,
Condition
FROM
#PatientTable
WHERE
ID = (SELECT ID AS NumberOfClaims FROM #PatientTable GROUP BY ID HAVING ID = ID AND COUNT(*) = 1)
This will return
+----+---------+--------------+
| ID | ClaimID | Condition |
+----+---------+--------------+
| B | 23853 | Hypertension |
+----+---------+--------------+
So far so good, but there is another issue we may face. Let's say you have a multiple Claims from a multiple patients, using this query as is will only show one patient. To show all patients we need to use IN rather than = under the WHERE clause
WHERE
ID IN (SELECT ID AS NumberOfClaims FROM #PatientTable GROUP BY ID HAVING ID = ID AND COUNT(*) = 1)
This will list all patients that falls under this condition.
If you need more conditions to filter, you just add them to the WHERE clause and you'll be good to go.
SELECT id, sum(ct)
FROM (SELECT customer_id, CASE WHEN category = 'X' THEN 0 else 1
end ct
FROM MASTER_TABLE
) AS t1
GROUP BY id
HAVING sum(ct) = 0
id which will have sum(ct) more than 1, will have multiple conditions
Use joins instead of subquery. Joins are always better in performance. You can use below query.
SELECT T1.id, T1.claimid, T1.Condition
FROM mytable T1
INNER JOIN
(
select id, count(Condition) counter
from mytable
group by id HAVING COUNT(DISTINCT CONDITION)=1
) T2 ON T1.ID=T2.ID
WHERE T2.counter=1

Find most recent record by date

This is my original data (anonymised):
id usage verified date
1 4000 Y 2015-03-20
2 5000 N 2015-06-20
3 6000 N 2015-07-20
4 7000 Y 2016-09-20
Original query:
SELECT
me.usage,
mes.verified,
mes.date
FROM
Table1 me,
Table2 mes,
Table3 m,
Table4 mp
WHERE
me.theFk=mes.id
AND mes.theFk=m.id
AND m.theFk=mp.id
How would I go about selecting the most recent verified and non-verified?
So I would be left with:
id usage verified date
1 6000 N 2015-07-20
2 7000 Y 2016-09-20
I am using Microsoft SQL Server 2012.
First, do not use implicit joins. This was discontinued more than 10 years ago.
Second, embrace the power of the CTE, the in clause and row_number:
with CTE as
(
select
me.usage,
mes.verified,
mes.date,
row_number() over (partition by Verified order by Date desc) as CTEOrd
from Table1 me
inner join Table2 mes
on me.theFK = mes.id
where mes.theFK in
(
select m.id
from Table3 m
inner join Table4 mp
on mp.id = m.theFK
)
)
select CTE.*
from CTE
where CTEOrd = 1
You can select the TOP 1 ordered by date for verified=N, union'd with the TOP 1 ordered by date for verified=Y.
Or in pseudo SQL:
SELECT TOP 1 ...fields ...
FROM ...tables/joins...
WHERE Verified = 'N'
ORDER BY Date DESC
UNION
SELECT TOP 1 ...fields ...
FROM ...tables/joins...
WHERE Verified = 'Y'
ORDER BY Date DESC
drop table #stack2
CREATE TABLE #stack2
([id] int, [usage] int, [verified] varchar(1), [date] datetime)
;
INSERT INTO #stack2
([id], [usage], [verified], [date])
VALUES
(1, 4000, 'Y', '2015-03-20 00:00:00'),
(2, 5000, 'N', '2015-06-20 00:00:00'),
(3, 6000, 'N', '2015-07-20 00:00:00'),
(4, 7000, 'Y', '2016-09-20 00:00:00')
;
;with cte as (select verified,max(date) d from #stack2 group by verified)
select row_number() over( order by s2.[verified]),s2.[usage], s2.[verified], s2.[date] from #stack2 s2 join cte c on c.verified=s2.verified and c.d=s2.date
As per the data shown i had written the query.
for your scenario this will be use full
WITH cte1
AS (SELECT me.usage,
mes.verified,
mes.date
FROM Table1 me,
Table2 mes,
Table3 m,
Table4 mp
WHERE me.theFk = mes.id
AND mes.theFk = m.id
AND m.theFk = mp.id),
cte
AS (SELECT verified,
Max(date) d
FROM cte1
GROUP BY verified)
SELECT Row_number()
OVER(
ORDER BY s2.[verified]),
s2.[usage],
s2.[verified],
s2.[date]
FROM cte1 s2
JOIN cte c
ON c.verified = s2.verified
AND c.d = s2.date
You can as the below Without join.
-- Mock data
DECLARE #Tbl TABLE (id INT, usage INT, verified CHAR(1), date DATETIME)
INSERT INTO #Tbl
VALUES
(1, 4000 ,'Y', '2015-03-20'),
(2, 5000 ,'N', '2015-06-20'),
(3, 6000 ,'N', '2015-07-20'),
(4, 7000 ,'Y', '2016-09-20')
SELECT
A.id ,
A.usage ,
A.verified ,
A.MaxDate
FROM
(
SELECT
id ,
usage ,
verified ,
date,
MAX(date) OVER (PARTITION BY verified) MaxDate
FROM
#Tbl
) A
WHERE
A.date = A.MaxDate
Result:
id usage verified MaxDate
----------- ----------- -------- ----------
3 6000 N 2015-07-20
4 7000 Y 2016-09-20
CREATE TABLE #Table ( ID INT ,usage INT, verified VARCHAR(10), _date DATE)
INSERT INTO #Table ( ID , usage , verified , _date)
SELECT 1,4000 , 'Y','2015-03-20' UNION ALL
SELECT 2, 5000 , 'N' ,'2015-06-20' UNION ALL
SELECT 3, 6000 , 'N' ,'2015-07-20' UNION ALL
SELECT 4, 7000 , 'Y' ,'2016-09-20'
SELECT ROW_NUMBER() OVER(ORDER BY usage) ID,usage , A.verified , A._date
FROM #Table
JOIN
(
SELECT verified , MAX(_date) _date
FROM #Table
GROUP BY verified
) A ON #Table._date = A._date

SQL DISTINCT id when SUM weight in sub query

I have two types of sub query's in the statement.
First of all some sample data.
Table
CAT ID Weight GROUP
1 1 200 A
1 2 300 B
1 3 250 B
1 1 200 A
1 4 200 A
One sub query is a count of distinct IDs which works as expected.
( SELECT COUNT (distinct t1.ID)
FROM table t1
WHERE t1.group = 'A'
GROUP BY t1.cat)
AS [count],
The other sub query is a sum of the weight
( SELECT SUM(t1.weight)
FROM table t1
WHERE t1.group = 'A'
GROUP BY t1.cat)
AS [weight],
This doesn't give me what i need as it will total 600 when I want it to total 400 as i want only to use unique ID's as the first query does.
However by adding distinct...
( SELECT SUM(DISTINCT t1.weight)
FROM table t1
WHERE t1.group = 'A'
GROUP BY t1.cat)
AS [weight],
This only returns 200 as it is using distinct weight, what i want is it to use distinct ID in this, but how can i do this while still only selecting the weight?
Something like (logically speaking as this doesn't work)
( SELECT SUM(t1.weight)
FROM table t1
WHERE t1.group = 'A'
AND t1.ID IS DISTINCT
GROUP BY t1.cat)
AS [weight],
SELECT cat,SUM(weight) AS [weight] FROM
(SELECT *,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID) as rn
FROM table ) as tbl
WHERE [group] = 'A' AND rn=1
GROUP BY cat
I might be missing something, as I've looked at your sample data and what I believe is your desired output, but can you not just do a simple GROUP BY and SUM:
CREATE TABLE SampleData
([CAT] int, [ID] int, [Weight] int, [GROUP] varchar(1))
;
INSERT INTO SampleData
([CAT], [ID], [Weight], [GROUP])
VALUES
(1, 1, 200, 'A'),
(1, 2, 300, 'B'),
(1, 3, 250, 'B'),
(1, 1, 200, 'A'),
(1, 4, 200, 'A')
;
SELECT ID, COUNT(ID) AS [Counter], SUM(Weight) AS SumWeight
FROM SampleData
WHERE [GROUP] = 'A'
GROUP BY ID
To produce:
ID Counter SumWeight
1 2 400
4 1 200

SQL Server Distinct Question

I need to be able to select only the first row for each name that has the greatest value.
I have a table with the following:
id name value
0 JOHN 123
1 STEVE 125
2 JOHN 127
3 JOHN 126
So I am looking to return:
id name value
1 STEVE 125
2 JOHN 127
Any idea on the MSSQL Syntax on how to perform this operation?
While you specified SQL Server, you did not specify the version. If you are using SQL Server 2005 or later, you can do something like:
With RankedItems As
(
Select id, name, value
, Row_Number() Over ( Partition By name Order By value Desc, id Asc ) As ItemRank
From Table
)
Select id, name, value
From RankedItems
Where ItemRank = 1
try:
SELECT
MIN(id) as id,dt.name,dt.value
FROM (SELECT
name,MAX(value) as value
FROM YourTable
GROUP BY name
) dt
INNER JOIN YourTable t ON dt.name=t.name and dt.value=t.value
GROUP BY dt.name,dt.value
try it out:
DECLARE #YourTable table (id int, name varchar(10), value int)
INSERT #YourTable VALUES (0, 'JOHN', 123)
INSERT #YourTable VALUES (1, 'STEVE', 125)
INSERT #YourTable VALUES (2, 'JOHN', 127)
INSERT #YourTable VALUES (3, 'JOHN', 126)
--extra data not in the question, shows why you need the outer group by
INSERT #YourTable VALUES (4, 'JOHN', 127)
INSERT #YourTable VALUES (5, 'JOHN', 127)
INSERT #YourTable VALUES (6, 'JOHN', 127)
INSERT #YourTable VALUES (7, 'JOHN', 127)
SELECT
MIN(id) as id,dt.name,dt.value
FROM (SELECT
name,MAX(value) as value
FROM #YourTable
GROUP BY name
) dt
INNER JOIN #YourTable t ON dt.name=t.name and dt.value=t.value
GROUP BY dt.name,dt.value
ORDER BY id
output:
id name value
----------- ---------- -----------
1 STEVE 125
2 JOHN 127
(2 row(s) affected)
You could do something like
SELECT id, name, value
FROM (SELECT id, name, value
ROWNUMBER() OVER (PARTITION BY name ORDER BY value DESC) AS r
FROM table) AS x
WHERE x.r = 1 ;
This will not work in SQL Server 2000 and earlier, but it will be incredibly fast in SQL Server 2005 and 2008
How about:
SELECT a.id, a.name, b.maxvalue
FROM mytbl a
INNER JOIN (SELECT id, max(value) as maxvalue
FROM mytbl
GROUP BY id) b ON b.id = a.id
SELECT a.id, a.name, a.value
FROM mytbl a
INNER JOIN (SELECT name, max(value) as maxvalue
FROM mytbl
GROUP BY name) b ON b.name = a.name and b.maxvalue = a.value