Sum the number of occurrence by id - sql

Is it possible to COUNT the number of times a value occurs in a table, however, use the count of 1 if the value appears more than once for each id.
Take the below table as an example. We want to see if either {5,6} occurred for p_id. If more than 1 occurrence of {5,6} is found, treat it as 1. For eg. p_id 1, the total count is 1.
p_id status
1 5
1 6
1 2
2 5
2 5
3 4
3 2
4 6
4 2
4 5
..transforms to..
p_id count
1 1
2 1
3 0
4 1
COUNT(CASE status IN (5,6) THEN 1 END) does an overall count.

Use the CASE...WHEN... as follows:
SELECT a.id, ISNULL(b.cnt, 0)
FROM
(
SELECT DISTINCT id FROM tab
) a
LEFT JOIN
(
SELECT id, CASE COUNT(*) WHEN 1 THEN 0 ELSE 1 END 'cnt'
FROM tab WHERE val in (5, 6) GROUP BY id
) b
ON a.id = b.id
SQLFiddle

This solution provides a quick setup and a simple two-step explanation of how I do this, using your example. The second query provides the desired result:
CREATE TABLE #temp (p_id INT, [status] INT);
INSERT #temp VALUES (1,5);
INSERT #temp VALUES (1,6);
INSERT #temp VALUES (1,2);
INSERT #temp VALUES (2,5);
INSERT #temp VALUES (2,5);
INSERT #temp VALUES (3,4);
INSERT #temp VALUES (3,2);
INSERT #temp VALUES (4,6);
INSERT #temp VALUES (4,2);
INSERT #temp VALUES (4,5);
-- Simple two-step tutorial
-- First, group by p_id so that all p_id's will be shown
-- run this to see...
SELECT A.p_id
FROM #temp A
GROUP BY A.p_id;
-- Now expand your query
-- Next, for each p_id row found, perform sub-query to see if 1 or more exist with status=5 or 6
SELECT A.p_id
,CASE WHEN EXISTS(SELECT 1 FROM #temp B WHERE B.p_id=A.p_id AND [status] IN (5,6)) THEN 1 ELSE 0 END AS [Count]
FROM #temp A
GROUP BY A.p_id;

Use the SIGN() function. It is exactly what you are looking for.
SELECT
[p_id],
SIGN(COUNT(CASE WHEN [status] IN (5,6) THEN 1 END)) AS [count]
FROM #temp
GROUP BY p_id

You can translate 5,6 = 1 and rest to 0 then do max()
with cte as (
select p_id, case when status in (5,6) then 1 else 0 end status
from FROM #tem)
select p_id, max(status) status
from cte
group by p_id

Related

How to the result set form the below table

Table 1 contains certain set of data's. I need to get the following result set form the Table 1
Table1
Id Desc ParentId
1 Cloths 0
2 Mens 1
3 Womens 1
4 T-Shirt_M 2
5 Casual Shirts_M 2
6 T-Shirt_F 3
7 Education 8
If I pass a parameter as "Casual Shirts_M" I should get the below result set.
Result Set
Id Desc ParentId
1 Cloths 0
2 Mens 1
5 Casual Shirts_M 2
As mentioned in comments, there are plenty of Recursive Common Table Expressions examples for this, here's another one
DECLARE #Desc NVARCHAR(50) = 'Casual Shirts_M'
;WITH cteX
AS
( SELECT
B.Id, B.[DESC], B.ParentId
FROM
Table1 b
WHERE
B.[Desc] = #Desc
UNION ALL
SELECT
E.Id, E.[DESC], E.ParentId
FROM
Table1 E
INNER JOIN
cteX r ON e.Id = r.ParentId
)
SELECT * FROM cteX ORDER BY ID ASC
SQL-Fiddle provided by #WhatsThePoint
The question comes under the concept of Building hierarchy using Recursive CTE:
CREATE TABLE cloths
(
id INT,
descr VARCHAR(100),
parentid INT
);
insert into cloths values (1,'Cloths',0);
insert into cloths values (2,'Mens',1);
insert into cloths values (3,'Womens',1);
insert into cloths values (4,'T-Shirt_M',2);
insert into cloths values (5,'Casual Shirts_M',2);
insert into cloths values (6,'T-Shirt_F',3);
insert into cloths values (7,'Education',8);
DECLARE #variety VARCHAR(100) = 'Casual Shirts_M';
WITH
cte1 (id, descr, parentid)
AS (SELECT *
FROM cloths
WHERE descr = #variety
UNION ALL
SELECT c.id,
c.descr,
c.parentid
FROM cloths c
INNER JOIN cte1 r
ON c.id = r.parentid)
SELECT *
FROM cte1
ORDER BY parentid ASC;

Add Min Value on Query Output in Separate Column

I have the following table:
No Item Value
----------------------------
1 A 5
2 B 8
3 C 9
If I use Min function on Value field, then I'll get 5.
My question is, how can I put the MIN value into a new column? Like the following result:
No Item Value newCol
----------------------------
1 A 5 5
2 B 8 5
3 C 9 5
Is it possible to do that?
Thank you.
Something like:
select No, Item, Value, (select min(value) from table)
from table
should do it.
I'd prefer to do the subquery in a join, you'll have to name the field. Something like this;
Sample Data
CREATE TABLE #TestData (No int, item nvarchar(1), value int)
INSERT INTO #TestData (No, item, value)
VALUES
(1,'A',5)
,(2,'B',8)
,(3,'C',9)
Query
SELECT
td.No
,td.item
,td.value
,a.Min_Value
FROM #TestData td
CROSS JOIN
(
SELECT
MIN(Value) Min_Value
FROM #TestData
) a
Result
No item value Min_Value
1 A 5 5
2 B 8 5
3 C 9 5
You could do that even simpler by using an appropriate OVER() clause.
SELECT *
, MIN(Value) OVER () AS [newCol]
FROM Table
This would be simpler and less resource consuming than a (SELECT MIN(Value) FROM TABLE) in the top level SELECT.
Sample code:
DECLARE #tbl TABLE (No int, Item char(1), Value int)
INSERT #tbl VALUES (1, 'A', 5), (2, 'B', 8), (3, 'C', 9)
SELECT *
, MIN(Value) OVER () AS [newCol]
FROM #tbl
Using cross join with min value from table :
SELECT * FROM #Tbl1 CROSS JOIN (SELECT MIN(Value1) Value1 FROM #Tbl1) A

Strange behavior OFFSET ... ROWS FETCH NEXT ... ROWS ONLY after ORDER BY

I see very strange behavior of construction like
SELECT ... ORDER BY ... OFFSET ... ROWS FETCH NEXT ... ROWS ONLY
I made simple example to display it (SQL Server version: 2012 - 11.0.5058.0 (X64) ):
CREATE TABLE #temp( c1 int NOT NULL, c2 varchar(MAX) NULL)
INSERT INTO #temp (c1,c2) VALUES (1,'test1')
INSERT INTO #temp (c1,c2) VALUES (2,NULL)
INSERT INTO #temp (c1,c2) VALUES (3,'test3')
INSERT INTO #temp (c1,c2) VALUES (4,NULL)
INSERT INTO #temp (c1,c2) VALUES (5,NULL)
First query:
select * from #temp
ORDER BY c2 DESC
Result is ok:
3 test3
1 test1
2 NULL
4 NULL
5 NULL
Second query:
select * from #temp
ORDER BY c2 DESC
OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY
Result has another sorting:
3 test3
1 test1
4 NULL
And last query:
select * from #temp
ORDER BY c2 DESC
OFFSET 3 ROWS FETCH NEXT 3 ROWS ONLY
Result is very strange and invalid in my opinion (I need get two records that not contains in previous result, but instead I get record with id=4 second time):
4 NULL
2 NULL
Can anyone explain why SQL server works so strange?
Use unique key column with this type of case:
select * from #temp
ORDER BY c2, c1 DESC
OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY
Never faced ordering problem.

SQL if statement with two tables

Given the following tables:
table objects
id Name rating
1 Megan 9
2 Irina 10
3 Vanessa 7
4 Samantha 9
5 Roxanne 1
6 Sonia 8
swap table
id swap_proposalid counterpartyid
1 4 2
2 3 2
Everyone wants the ten. I would like to make a list for Irina of possible swaps where id 4 and 3 don't appear because the propositions are already there.
output1
id Name rating
1 Megan 9
5 Roxanne 1
6 Sonia 8
Thanks
This should do the trick:
SELECT o.id, o.Name, o.rating
FROM objects o
LEFT JOIN swap s on o.id = s.swap_proposalid
WHERE s.id IS NULL
AND o.Name != 'Irina'
This works
SELECT mt2.ID, mt2.Name, mt2.Rating
FROM [MyTable] mt2 -- Other Candidates
, [MyTable] mt1 -- Candidate / Subject (Irina)
WHERE mt2.ID NOT IN
(
SELECT st.swap_proposalid
FROM SwapTable st
WHERE
st.counterpartyid = mt1.ID
)
AND mt1.ID <> mt2.ID -- Don't match Irina with Irina
AND mt1.Name = 'Irina' -- Find other swaps for Irina
-- Test Data
CREATE TABLE MyTable
(
ID INT,
Name VARCHAR(100),
Rating INT
)
GO
CREATE TABLE SwapTable
(
ID INT,
swap_proposalid INT,
counterpartyid INT
)
GO
INSERT INTO MyTable VALUES(1 ,'Megan', 9)
INSERT INTO MyTable VALUES(2 ,'Irina', 10)
INSERT INTO MyTable VALUES(3 ,'Vanessa', 7)
INSERT INTO MyTable VALUES(4 ,'Samantha', 9)
INSERT INTO MyTable VALUES(5 ,'Roxanne', 1)
INSERT INTO MyTable VALUES(6 ,'Sonia', 8)
INSERT INTO SwapTable(ID, swap_proposalid, counterpartyid)
VALUES (1, 4, 2)
INSERT INTO SwapTable(ID, swap_proposalid, counterpartyid)
VALUES (1, 3, 2)
Guessing that the logic involves identifying the objects EXCEPT the highest rated object EXCEPT propositions with the highest rated object e.g. (using sample DDL and data kindly posted by #nonnb):
WITH ObjectHighestRated
AS
(
SELECT ID
FROM MyTable
WHERE Rating = (
SELECT MAX(T.Rating)
FROM MyTable T
)
),
PropositionsForHighestRated
AS
(
SELECT swap_proposalid AS ID
FROM SwapTable
WHERE counterpartyid IN (SELECT ID FROM ObjectHighestRated)
),
CandidateSwappersForHighestRated
AS
(
SELECT ID
FROM MyTable
EXCEPT
SELECT ID
FROM ObjectHighestRated
EXCEPT
SELECT ID
FROM PropositionsForHighestRated
)
SELECT *
FROM MyTable
WHERE ID IN (SELECT ID FROM CandidateSwappersForHighestRated);

find records for user that are not contiguous

I am having trouble figuring out how to even start this query.
I have a table that has the following columns and data:
User BeginMile EndMile
1 1 5
1 5 6
1 6 20
1 20 25
1 25 29
2 1 9
2 15 20
3 1 2
3 6 10
3 10 12
I need to first find where there are gaps for each user from the EndMile of the previous record, to the BeginMile of the next record. I then need to return the record before and after where the gap occurs for each user.
In the previous data example, I would like the following returned:
User PrevBeginMile PrevEndMile AfterBeginMile AfterEndMile Gap
2 1 9 15 20 6
3 1 2 6 10 4
How can this be done?
Considering you're on SQL 2005, this should work:
DECLARE #Runners TABLE (Id INT, BeginMile INT, EndMile INT)
INSERT INTO #Runners VALUES (1,1,5)
INSERT INTO #Runners VALUES (1,5,6)
INSERT INTO #Runners VALUES (1,6,20)
INSERT INTO #Runners VALUES (1,20,25)
INSERT INTO #Runners VALUES (1,25,29)
INSERT INTO #Runners VALUES (2,1,9)
INSERT INTO #Runners VALUES (2,15,20)
INSERT INTO #Runners VALUES (3,1,2)
INSERT INTO #Runners VALUES (3,6,10)
INSERT INTO #Runners VALUES (3,10,12)
WITH OrderedUsers AS (
SELECT *
, ROW_NUMBER() OVER (PARTITION BY Id ORDER BY BeginMile) RowNum
FROM #Runners
)
SELECT a.Id [User]
, a.BeginMile PrevBeginMile
, a.EndMile PrevEndMile
, b.BeginMile AfterBeginMile
, b.EndMile AfterEndMile
, b.BeginMile - a.EndMile Gap
FROM OrderedUsers a
JOIN OrderedUsers b
ON a.Id = b.Id
AND a.EndMile <> b.BeginMile
AND a.RowNum = b.RowNum - 1
Other than using RowNumber() [as in other answers], you could use...
SELECT
[current].User,
[current].BeginMile AS [PrevBeginMile],
[current].EndMile AS [PrevEndMile],
[next].BeginMile AS [AfterBeginMile],
[next].EndMile AS [AfterEndMile],
[next].BeginMile - [current].EndMile AS [Gap]
FROM
myTable AS [current]
CROSS APPLY
(SELECT TOP 1 * FROM myTable WHERE user = [current].User AND BeginMile > [current].BeginMile ORDER BY BeginMile ASC) AS [next]
WHERE
[current].EndMile <> [next].BeginMile
Or possibly...
FROM
myTable AS [current]
INNER JOIN
myTable AS [next]
ON [next].BeginMile != [current].EndMile
AND [next].BeginMile = (
SELECT
MIN(BeginMile)
FROM
myTable
WHERE
user = [current].User
AND BeginMile > [current].BeginMile
)
How about
WITH acte(user,beginmile,endmile) AS
(
SELECT user,start,end
ROW_NUMBER() OVER(PARTITION BY user ORDER BY START ASC) rownum
FROM mytable
)
SELECT base.user,base.beginmile,base.endmile,base.BeginMile - lead.EndMile Gap
FROM acte base
LEFT JOIN acte lead on base.id=lead.id AND base.rownum=lead.rownum-1
WHERE base.BeginMile - lead.EndMile > 0