SQL DISTINCT id when SUM weight in sub query - sql

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

Related

Lookup a value on another table with no relationship

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

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

how to use SQL group to filter rows with maximum date value

I have the following table
CREATE TABLE Test
(`Id` int, `value` varchar(20), `adate` varchar(20))
;
INSERT INTO Test
(`Id`, `value`, `adate`)
VALUES
(1, 100, '2014-01-01'),
(1, 200, '2014-01-02'),
(1, 300, '2014-01-03'),
(2, 200, '2014-01-01'),
(2, 400, '2014-01-02'),
(2, 30 , '2014-01-04'),
(3, 800, '2014-01-01'),
(3, 300, '2014-01-02'),
(3, 60 , '2014-01-04')
;
I want to achieve the result which selects only Id having max value of date. ie
Id ,value ,adate
1, 300,'2014-01-03'
2, 30 ,'2014-01-04'
3, 60 ,'2014-01-04'
how can I achieve this using group by? I have done as follows but it is not working.
Select Id,value,adate
from Test
group by Id,value,adate
having adate = MAX(adate)
Can someone help with the query?
Select the maximum dates for each id.
select id, max(adate) max_date
from test
group by id
Join on that to get the rest of the columns.
select t1.*
from test t1
inner join (select id, max(adate) max_date
from test
group by id) t2
on t1.id = t2.id and t1.adate = t2.max_date;
Please try:
select
*
from
tbl a
where
a.adate=(select MAX(adate) from tbl b where b.Id=a.Id)
If you are using a DBMS that has analytical functions you can use ROW_NUMBER:
SELECT Id, Value, ADate
FROM ( SELECT ID,
Value,
ADate,
ROW_NUMBER() OVER(PARTITION BY ID ORDER BY Adate DESC) AS RowNum
FROM Test
) AS T
WHERE RowNum = 1;
Otherwise you will need to use a join to the aggregated max date by Id to filter the results from Test to only those where the date matches the maximum date for that Id
SELECT Test.Id, Test.Value, Test.ADate
FROM Test
INNER JOIN
( SELECT ID, MAX(ADate) AS ADate
FROM Test
GROUP BY ID
) AS MaxT
ON MaxT.ID = Test.ID
AND MaxT.ADate = Test.ADate;
I would try something like this
Select t1.Id, t1.value, t1.adate
from Test as t1
where t1.adate = (select max(t2.adate)
from Test as t2
where t2.id = t1.id)

Next/previous record based on current

I have a table which is not sorted by any of column. Is there any way to select next/previous record if I know only Id of current? (I'm using mssql)
Id Label Date
---------------------
1 label1 2011-01-10
7 label2 2011-01-15 -- how to get previous?
5 label3 2011-01-12 -- I know id of this record
10 label10 2011-01-25 -- how to get next?
12 label8 2011-01-13
2 label5 2011-01-29
Thanks in advance!
try this:
VALUES (1, 'label1', '2011-01-10'), (7, 'label2', '2011-01-15'),
(5, 'label3', '2011-01-12'), (10, 'label10', '2011-01-25'),
(12, 'label8', '2011-01-13'), (2, 'label5', '2011-01-29')
select * from table007;
Declare #inptID int=12;
;WITH CTE
as
(
select *, ROW_NUMBER() over (order by (select 0)) as rn
from table007
)
select *
from CTE
where rn in( select rn-1 from CTE where id = #inptID)
union all
select * from CTE where rn in(select rn + 1 from CTE where id = #inptID);
SQL Fiddle Demo
DEMO
If it is not sorted by any column, there is no definitive next or previous record. Data in SQL Server has no order, other than that specified by an ORDER BY clause.
If you really want the previous from the list you enclosed, here is a way.
declare #t table(Id int, Label varchar(10), Date date, s int identity(1,1))
insert #t (id, label, date)
values(1,'label1','2011-01-10'),(7,'label2','2011-01-15'),
(5,'label3','2011-01-12'),(10,'label10','2011-01-25'),
(12,'label8','2011-01-13'),(2,'label5','2011-01-29')
--select the data with a self join
select t1.id as previous_id, t2.id, t2.Label, t2.Date, t3.id, t3.id as next_id
from #t t1
right join
#t t2 on t1.s + 1 = t2.s
left join
#t t3 on t2.s = t3.s - 1

Interesting SQL issue

I have a SQL problem I am trying to digest. I am using SQL Server 2005.
In a table I have data as such:
ID Type
1 A
2 A
3 A
3 B
4 B
I need to find all of the IDs that have a Type of both A and B.
Use the INTERSECT operator:
SELECT DISTINCT ID FROM [Table] WHERE Type = 'A'
INTERSECT
SELECT DISTINCT ID FROM [Table] WHERE Type = 'B'
select distinct a.id
from table a
join table b on a.id=b.id
where a.type='A'
and b.type='B';
With a semi-join (no sorting, only index seek on B):
select a.id from table a
where a.type = 'A'
and exists (select * from table b where a.id = b.id and b.type = 'B')
If you want to abstract the problem a little bit and find cases where rows with the same id contain different values in the type column, you can check for <> like this:
DECLARE #TestTable TABLE (thisid int, thisval varchar(1))
INSERT INTO #TestTable VALUES (1, 'A')
INSERT INTO #TestTable VALUES (2, 'A')
INSERT INTO #TestTable VALUES (3, 'A')
INSERT INTO #TestTable VALUES (3, 'B')
INSERT INTO #TestTable VALUES (4, 'B')
SELECT DISTINCT thisid
FROM #TestTable a
WHERE EXISTS
( SELECT *
FROM #TestTable b
WHERE a.thisid=b.thisid AND a.thisval<>b.thisval)
-- www.caliberwebgroup.com
This returns:
3
select id, count(type = 'A') as a_count, count(type = 'B') as b_count
from your_table
group by 1
having a_count > 0 and b_count > 0;
At least, this works in sane SQL environments. Dunno if it works in yours.
I was not looking at other answers, but still posting. lol
SELECT distinct t1.ID
FROM table1 AS t1
WHERE exists
(select t2.ID from table1 t2 where t2.type="A" and t2.ID=t1.ID)
and exists
(select t3.ID from table1 t3 where t3.type="B" and t3.ID=t1.ID);
SELECT Id FROM tableX AS x, tableX AS y
WHERE x.id = y.id AND x.type = 'A' AND y.type = 'B'
This is very simple
Declare #t table([ID] INT, [Type] VARCHAR(2))
INSERT INTO #t SELECT 1, 'A' UNION ALL SELECT 2,'A' UNION ALL SELECT 3,'A'
UNION ALL SELECT 3,'B' UNION ALL SELECT 4,'B' UNION ALL SELECT 5,'A' UNION ALL SELECT 5,'A'
;WITH CTE AS
(
SELECT Rn = Row_NUMBER() OVER(PARTITION BY [ID],[TYPE] ORDER BY [ID])
,*
FROM #t
)
SELECT ID
FROM CTE
WHERE Rn =1 AND ([Type]='A' or [Type]='B')
GROUP BY [ID]
HAVING (COUNT([ID])>1)
Output:
id
3
this would help if there are "unknown" amounts of types and you want to find all IDs which have all of types
select id from yourtable group by id having count(*)=(select count(distinct type) from yourtable)
select id
from idtypedata
group by id
having
sum(
case type
when 'A' then 1
when 'B' then 2
-- when 'C' then 4
-- when 'D' then 8
end
) & 1 = 1
And
sum(
case type
when 'A' then 1
when 'B' then 2
-- when 'C' then 4
-- when 'D' then 8
end
) & 2 = 2