I'm using SQL Server 2008 R2
I have a complex query which I need to have a conditionally summed column for it.
Here is a simplified version of my query and results:
DECLARE #t TABLE (id int, condition int, value int);
INSERT INTO #t
VALUES (1,1,12), (2,0,88), (3,1,11)
SELECT
*,
(SELECT SUM(value) FROM #t WHERE condition = 1) as SumOfConditionalValues
FROM #t
Here are the results of this query"
id condition value SumOfConditionalValues
1 1 12 23
2 0 88 23
3 1 11 23
I can't afford the SumOfConditionalValues sub query.
Is there an elegant way to achieve the conditionally summed column without it?
Which aggregate commands are suitable here, if any, and how do I apply these?
Try this:
SELECT *, SUM(CASE WHEN condition = 1 THEN value END) OVER() SumOfConditionalValues
FROM #t
See here: http://sqlfiddle.com/#!3/1abea/1
Use a self join:
CREATE TABLE MyTable (id int, condition int, value int);
INSERT INTO MyTable
VALUES (1,1,12), (2,0,88), (3,1,11)
SELECT
MyTable.id,
MyTable.Condition,
MyTable.value,
SUM(JoinedMyTable.Value)
FROM
MyTable
LEFT JOIN MyTable JoinedMyTable ON MyTable.condition = JoinedMyTable.Condition
GROUP BY
MyTable.id,
MyTable.Condition,
MyTable.value
EDIT: Don't know if you want every row to show the sum of rows where condition = 1, but if you do just change the join clause to be:
LEFT JOIN MyTable JoinedMyTable ON JoinedMyTable.Condition = 1
I believe what you are looking for is the "CASE' statement...Very powerful, for example:
select id, sum(case when condition=1 then value else 0 end) group by id..etc
Declare #sum int
SELECT #sum=SUM(value) FROM #t WHERE condition = 1
select *,#sum from yourtable
You can bring the results aggregated by condition:
DECLARE #t TABLE (id int, condition int, value int);
INSERT INTO #t VALUES (1,1,12), (2,0,88), (3,1,11)
SELECT *,
sum(value) over (partition by condition) as SumOfConditionalValues
FROM #t
I think this is what you want:
SELECT id, condition, value,
SUM(CASE WHEN condition = 1 THEN value_at_1 END) OVER() SumOfConditionalValues
FROM (select *,
(case when condition = 1 then value end) as value_at_1
from #t
) t
You need a "conditional value", which you can create in a subquery.
Related
I have 2 tables, the first one has 10 distinct values:
,
each GlobalPnID has many values on the second table, I want to join 2 tables and select one random value of PortionKey of the second table that match the condition and move to the next GlobalPnID
SELECT TOP 10 gpnp.PortionKey, tt.GlobalPnID
from #TempTable tt
LEFT JOIN [dbo].[GlobalPartNumberPortions] gpnp ON gpnp.GlobalPnId = tt.GlobalPnID
-- tt is the first table
-- gpnp is the second
SELECT TT.GlobalPnID,X.PortionKey
FROM #TempTable AS TT
CROSS APPLY
(
SELECT TOP 1 R.PortionKey
FROM [dbo].[GlobalPartNumberPortions] AS R
WHERE R.GlobalPnId=TT.GlobalPnID
ORDER BY R.PortionID
)X
You could use Row_Number with a CTE and set the criteria you want, for example:
DECLARE #TempTable TABLE
(
globalpnid INT
)
DECLARE #GlobalPartNumberPortions TABLE
(
portionid INT,
portionkey NVARCHAR(10),
globalpnid INT
)
INSERT INTO #TempTable
(globalpnid)
VALUES (1),(2),(3),(4)
INSERT INTO #GlobalPartNumberPortions
(portionid,
portionkey,
globalpnid)
VALUES (1,'ABC',1),
(2,'XYZ',1),
(3,'AZZ',2),
(4,'QWE',3),
(5,'TYU',4);
WITH cteportion
AS (SELECT portionkey,
globalpnid,
rn = Row_number()
OVER (
partition BY globalpnid
ORDER BY RAND(CHECKSUM(NEWID()))))
FROM #GlobalPartNumberPortions)
SELECT gpnp.portionkey,
tt.globalpnid
FROM #TempTable tt
LEFT JOIN cteportion gpnp
ON tt.globalpnid = gpnp.globalpnid
AND gpnp.rn = 1
This will partition the second table by the globalpnid ordering on ORDER BY RAND(CHECKSUM(NEWID()))) and you can then use this in the join gpnp.rn = 1. In the example I've included, you'll see that GlobalPnID = 1 will alternate between ABC and XYZ.
Edit: as suggested by #Thorsten Kettner in the comment, you can order by RAND(CHECKSUM(NEWID())))
Following is my table:
I want to create a view with the following conditions in a select statement:
If count(employee_id) > 1 then only the record having status = 'Current' is picked in the view.
I tried:
select employee_id
, case when COUNT(employee_id) > 1 and statusval = 'Current' then 'Y' else 'N' end as val
from table1
group by employee_id
I hope someone can help me with this statement. Thanks.
Try this
DECLARE #T TABLE (ID INT, STATUS VARCHAR(50))
INSERT INTO #T VALUES (1,'Current'),(2,'Historical'),(2,'Historical'),(2,'Current')
SELECT * FROM #T M
INNER JOIN (SELECT ID FROM #T
GROUP BY ID
HAVING COUNT(ID) > 1) S ON S.ID=M.ID
WHERE M.STATUS = 'Current'
Just to show you how your attempt could have looked. You were nearly there, you just needed to use the windowed version of count e.g.
with cte as (
select Employee_Id, [Status]
, case when count(*) over (partition by id) > 1 and [Status] = 'Current' then 1 else 0 end Val
from Table1
)
select Employee_Id, [Status]
from cte
where Val = 1;
And this approach appears on the face of it to perform better than a join.
All of your sample data have exactly one row with 'Current'. So, the simplest solution appears to be:
select t.*
from t
where t.status = 'Current';
I am having input table like below:
and in a select query without alteration of table need an output like:
drop table if exists T;
create table T(id int, nm nvarchar(10))
GO
insert T(id, nm) values (1,'r'),(2,'r'),(3,null),(4,'r')
SELECT * FROM T
GO
-- solution:
select
id, nm,
CASE WHEN nm is not null then count(nm) over (order by id) ELSE NULL END
from T
GO
compare execution plan of all solutions (using SQL 2017) :-)
My solution 21%; LukStorms solution 38%; Ian-Fogelman solution 41%
Choose your solution after you test in your specific server!
You can calculate a row_number partitioned on whether "nm" is null, then only show the calculated row_number when "nm" is not null.
Example snippet:
declare #T table (id int identity(1,1) primary key, nm varchar(8));
insert into #T (nm) values ('R'),('R'),(null),('R');
select *,
iif(nm is null,null,row_number() over (partition by iif(nm is null,1,0) order by id)) as [Count]
from #T
order by id
;WITH CTE AS
(
SELECT ID,
ROW_NUMBER() OVER(PARTITION BY 1 ORDER BY ID) AS [COUNT]
FROM [TABLE] WHERE NM IS NOT NULL
)
SELECT S.ID,
S.NM,
CTE.[COUNT]
FROM [TABLE] AS S LEFT JOIN CTE AS CTE ON S.ID = CTE.ID
You can start with a CTE that adds a ROW_NUMBER() column and filters out rows WHERE 'nm' IS NULL.
Then SELECT from your table and OUTER JOIN to the CTE, using the ROW_NUMBER column to populate your Count column.
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
If I have a table like this:
id name value
1 abc 1
2 def 4
3 ghi 1
4 jkl 2
How can I select a new table that still has id, name, value but only the ones with a minimum value.
In this example I need this table back:
1 abc 1
3 ghi 1
Finding those values is pretty straightforward:
SELECT *
FROM YourTable
WHERE value = (SELECT MIN(Value) FROM YourTable);
As for the right syntax for putting those rows in another table, that will depend on the database engine that you are using.
An alternative to #Lamak's solution could be to use the rank window function. Depending on the exact scenario, it may perform quite better:
SELECT id, name, value
FROM (SELECT id, name, value, RANK() OVER (ORDER BY value ASC) AS rk
FROM mytable) t
WHERE rk = 1
not sure exactly if this is what you're trying to do, but I think this would work:
--creating #temp1 to recreate your table/example
CREATE TABLE #TEMP1
(id INT NOT NULL PRIMARY KEY,
name CHAR(3) NOT NULL,
value INT NOT NULL)
INSERT INTO #TEMP1
VALUES
(1,'abc',1),
(2,'def',4),
(3,'ghi',1),
(4,'jkl',2)
-verify correct
SELECT * FROM #temp1
--populate new table with min value from table 1
SELECT *
INTO #TEMP2
FROM #TEMP1
WHERE value = (SELECT MIN(value)
FROM #TEMP1)
SELECT * FROM #TEMP2