check all the values of a column - sql-server

select all the departments having students with even roll number
Dept No Roll No Student Name
1 1 lee
1 2 scott
2 2 scott
2 4 smith
1 4 smith
This should result in DEpt no 2 as it has only students with roll number divisible by 2

Another(imo easy and lightweight) way is using NOT EXISTS and DISTINCT:
SELECT DISTINCT [Dept No]
FROM dbo.TableName t
WHERE NOT EXISTS
(
SELECT 1 FROM dbo.TableName t2
WHERE t.[Dept No] = t2.[Dept No]
AND t2.[Roll No] % 2 = 1
)
Demo
If there is no odd number all must be even.

You can use GROUP BY with HAVING like this.
Query
SELECT [Dept No]
FROM departments
GROUP BY [Dept No]
HAVING SUM(CASE WHEN [Roll No] % 2 = 0 THEN 1 ELSE 0 END) > 1
AND SUM(CASE WHEN [Roll No] % 2 = 1 THEN 1 ELSE 0 END) = 0
Explanation
The query returns the departments if there a rollno which is even using SUM(CASE WHEN [Roll No] % 2 = 0 THEN 1 ELSE 0 END) > 1. If there is any rollno with odd roll no, SUM(CASE WHEN [Roll No] % 2 = 1 THEN 1 ELSE 0 END) will return non zero sum and that department will be excluded.

declare #t table (Dept int,Rno int,Student varchar(10))
insert into #t (Dept,Rno,Student)values (1,1,'lee'),(1,2,'scott'),(2,2,'scott'),(2,4,'smith'),(1,4,'smith')
SELECT Dept,Rno,Student
FROM (SELECT ROW_NUMBER () OVER (ORDER BY Rno DESC) row_number, Dept,Rno,Student
FROM #t) a WHERE (row_number%2) = 0

Related

How to make Row_number() based on condition?

I have list of sample data. Using this I need new column which having sequence number. But condition of this sequence number is if consecutively InRange column value 1 then only it generate sequence number.In between if InRange value 0 then again sequence number start from 1 and so on.
Below query which I have created but not return expected result.
CREATE TABLE #Result (ID INT,Value INT,InRange BIT)
INSERT INTO #Result
SELECT 1 ,211,0
UNION SELECT 2 ,205,1
UNION SELECT 3 ,214,0
UNION SELECT 4 ,202,1
UNION SELECT 5 ,204,1
UNION SELECT 6 ,203,1
UNION SELECT 7 ,209,0
UNION SELECT 8 ,216,0
UNION SELECT 9 ,205,1
UNION SELECT 10 ,224,0
Query:
SELECT *
,CASE WHEN InRange=1 THEN ROW_NUMBER()OVER(Order by Id asc) ELSE 0 END AS ExpectedColumn
FROM #Result
Expected result.
ID Value InRange ExpectedColumn
1 211 0 0
2 205 1 1
3 214 0 0
4 202 1 1
5 204 1 2
6 203 1 3
7 209 0 0
8 216 0 0
9 205 1 1
10 224 0 0
This is a gaps and islands problem, with the islands being each group of records to which you want to assign its own row number sequence. One straightforward way to handle this uses the difference in row numbers method:
WITH cte1 AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY ID) rn1
FROM #Result
WHERE InRange = 1
),
cte2 AS (
SELECT t1.*,
ROW_NUMBER() OVER (ORDER BY t1.ID) - t2.rn1 AS diff
FROM #Result t1
LEFT JOIN cte1 t2
ON t1.ID = t2.ID
)
SELECT ID, Value, InRange,
CASE WHEN InRange <> 0
THEN ROW_NUMBER() OVER (PARTITION BY diff ORDER BY ID)
ELSE 0 END AS ExpectedColumn
FROM cte2
ORDER BY ID;
Demo
with grouped_data as (
select
*,
count(case when InRange = 0 then 1 else null end) over(order by ID rows between unbounded preceding and current row) as group_number
from #Result
)
select
ID,
Value,
InRange,
row_number() over(partition by group_number order by ID) - 1 as expected_column
from grouped_data
order by ID;

Unable to figure out query

I have a Table organized as follows:
ID Group
1 I
1 O
1 M
2 I
2 N
2 O
3 M
4 I
5 O
5 M
I need to find all IDs are Group M and either group I or O.
In this case group 1 and 5.
Thanks!
You can use grouping with conditional aggregates in HAVING clause:
SELECT Id
FROM mytable
WHERE [Group] IN ('I', 'O', 'M')
GROUP BY Id
HAVING COUNT(CASE WHEN [Group] = 'M' THEN 1 END) > 0 AND
COUNT(CASE WHEN [Group] IN ('O', 'I') THEN 1 END) > 0
Demo here

Select distinct rows where all values in a column are the same SQL Server

I want to find Distinct FKIDs where IsProcessed is True for the same FKIDs.
e.g.
CID FKID DataField IsProcessed
1 1 Test 1
2 1 Test 1
3 2 Test 0
4 2 Test 1
5 3 Test 0
6 3 Test 0
7 4 Test 1
8 5 Test 0
9 6 Test 1
10 6 Test 1
11 6 Test 1
I would like to get the following FKIDs Returned 1,4,6
because these are FKIDs where isprocessed is true for all instances.
Any help would be greatly appreciated! Thanks!
You could use SUM() OVER and COUNT() OVER
;WITH CTE AS(
SELECT
*,
IsProcessedCount = SUM(CASE WHEN IsProcessed = 1 THEN 1 ELSE 0 END) OVER(PARTITION BY FKID),
cc = COUNT(*) OVER(PARTITION BY FKID)
FROM TEST
)
SELECT
DISTINCT FKID
FROM CTE
WHERE IsProcessedCount = cc
Using GROUP BY and HAVING:
SELECT FKID
FROM Test
GROUP BY FKID
HAVING COUNT(FKID) = COUNT(CASE WHEN IsProcessed = 1 THEN 1 ELSE NULL END)
Another way:
SELECT FKID
FROM Test
GROUP BY FKID
HAVING COUNT(FKID) = SUM(CAST(IsProcessed AS INT))
Just replace TEST with your table.
Another way, using a correlated not exists predicate:
select fkid from TableName t
where IsProcessed = 1
and not exists
(select 1 from TableName where IsProcessed = 0 and FKID = t.FKID)
group by fkid
or the same using not in:
select fkid from TableName t
where IsProcessed = 1
and fkid not in
(select fkid from TableName where IsProcessed = 0 and FKID = t.FKID)
group by fkid
SELECT FKID
FROM TableName
GROUP BY FKID
HAVING (MIN(IsProcessed) = 1)
Or
SELECT FKID
FROM TableName
GROUP BY FKID
HAVING (MIN(CAST(IsProcessed AS INT)) = 1)
If IsProcessed is not of numeric type:

Count of rows for 2 days

My data is in below format:
employee order id date
a 123 01/06/2013
b 124 02/06/2013
a 125 02/06/2013
a 129 02/06/2013
I need the data in below format:
employee day 1 day 2
a 1 2
b 0 1
Try this one -
DECLARE #temp TABLE
(
dtStart DATETIME
, employees CHAR(1)
)
INSERT INTO #temp (employees, dtStart) VALUES('a','01/06/2013')
INSERT INTO #temp (employees, dtStart) VALUES('a','01/06/2013')
INSERT INTO #temp (employees, dtStart) VALUES('b','02/06/2013')
SELECT
employees
, day1 = COUNT(CASE WHEN DAY(dtStart) = 1 THEN 1 END)
, day2 = COUNT(CASE WHEN DAY(dtStart) = 2 THEN 1 END)
FROM #temp
--WHERE dtStart BETWEEN '01/06/2013' AND '30/06/2013'
GROUP BY employees
Something along these lines should work (depending on whether you have a fixed amount of days or not):
select employee,
SUM(CASE WHEN date = '01/06/2013' THEN 1 ELSE 0 END) as day1,
SUM(CASE WHEN date = '02/06/2013' THEN 1 ELSE 0 END) as day2
from table
group by employee
select distinct employees,
SUM(CASE WHEN dtStart = '01/06/2013' THEN 1 ELSE 0 END) as day1,
SUM(CASE WHEN dtStart = '02/06/2013' THEN 1 ELSE 0 END) as day2
from yourTable
group by dtStart,employees
see your demo

Best way to aggregate data - Update query or subquery?

I'm trying to aggregate some customer order data into one table for analysis. The data is the number of products a customer orders, then trying to determine whether the order is a small, medium, or large order, then determine the total products they bought and the cost of the order by OrderSize.
Small order - 1 - 2 products
Medium order - 3 - 4 products
Large Order - >=5 products
Here is the data:
CustomerID OrderID OrderSize OrderTotal
1 800 1 $20
2 801 1 $10
3 802 4 $85
1 803 1 $30
2 804 8 $120
3 805 1 $40
Here is the table I am attempting to build (easier to just post an image):
I could run an update query like this to populate the table:
-- Build the table --
CREATE TABLE Customers (
CustomerID varchar(10) PRIMARY KEY,
SmallOrderCount int,
SmallOrderProducts int,
SmallOrderTotal money,
MedOrderCount int,
MedOrderProducts int,
MedOrderTotal money,
LargeOrderCount int,
LargeOrderProducts int,
LargeOrderTotal money
);
-- Insert the unique customers --
INSERT INTO Customers
SELECT CustomerID FROM Orders GROUP BY CustomerID;
-- Update to populate the order information --
UPDATE Customers
SET SmallOrderCount = (SELECT count(*) FROM Orders WHERE OrderSize BETWEEN 1 AND 2 AND Orders.CustomerID = Customers.CustomerID);
UPDATE Customers
SET SmallOrderProducts = (SELECT sum(OrderSize) FROM Orders WHERE OrderSize BETWEEN 1 AND 2 AND Orders.CustomerID = Customers.CustomerID);
UPDATE Customers
SET SmallOrderTotal = (SELECT sum(OrderTotal) FROM Orders WHERE OrderSize BETWEEN 1 AND 2 AND Orders.CustomerID = Customers.CustomerID);
Then I could repeat this for the remaining 6 columns.
However, this seems like a lot of work. Is there a way to populate my Customer table using a subquery that may be less work? Or is my approach above the most direct?
You can do all of it in one INSERT command with CASE statements:
INSERT INTO Customers
SELECT CustomerID
,sum(CASE WHEN OrderSize BETWEEN 1 AND 2 THEN 1 ELSE 0 END) AS SmallOrderCount
,sum(CASE WHEN OrderSize BETWEEN 1 AND 2 THEN OrderSize ELSE 0 END) AS SmallOrderProducts
,sum(CASE WHEN OrderSize BETWEEN 1 AND 2 THEN OrderTotal ELSE 0 END) AS SmallOrderTotal
,sum(CASE WHEN OrderSize BETWEEN 3 AND 4 THEN 1 ELSE 0 END) AS MedOrderCount
,sum(CASE WHEN OrderSize BETWEEN 3 AND 4 THEN OrderSize ELSE 0 END) AS MedOrderProducts
,sum(CASE WHEN OrderSize BETWEEN 3 AND 4 THEN OrderTotal ELSE 0 END) AS MedOrderTotal
,sum(CASE WHEN OrderSize > 4 THEN 1 ELSE 0 END) AS LargeOrderCount
,sum(CASE WHEN OrderSize > 4 THEN OrderSize ELSE 0 END) AS LargeOrderProducts
,sum(CASE WHEN OrderSize > 4 THEN OrderTotal ELSE 0 END) AS LargeOrderTotal
FROM Orders
GROUP BY CustomerID;
See this working demo for the full query on data.SE.
One insert should be enough:
INSERT INTO Customers(CustomerID,SmallOrderCount,SmallOrderProducts,
SmallOrderTotal)
SELECT a.CustomerID, COUNT(a.*) as cnt, sum(a.OrderSize) as OrderSize,
sum(a.OrderTotal) as OrderTotal
FROM Orders a
WHERE a.OrderSize BETWEEN 1 AND 2
GROUP BY a.CustomerID
The query above will insert only customers whose order size is between 1 and 2. If you need to insert others as well, you can use :
INSERT INTO Customers(CustomerID,SmallOrderCount,SmallOrderProducts,
SmallOrderTotal)
SELECT a.CustomerID,
COUNT(CASE WHEN a.OrderSize BETWEEN 1 AND 2 THEN 1 END) as cnt,
sum(CASE WHEN a.OrderSize BETWEEN 1 AND 2 THEN a.OrderSize ELSE 0 END) as OrderSize,
sum(CASE WHEN a.OrderSize BETWEEN 1 AND 2 THEN a.OrderTotal ELSE 0 END ) as OrderTotal
FROM Orders a
GROUP BY a.CustomerID
I'd only create a single query (which can be used as view) for that and not a whole new persisted table.
WITH cteOrders AS (
SELECT CustomerID,
CASE WHEN OrderSize BETWEEN 1 AND 2 THEN OrderSize END SmallOrderProducts,
CASE WHEN OrderSize BETWEEN 1 AND 2 THEN OrderTotal END SmallOrderTotal,
CASE WHEN OrderSize BETWEEN 3 AND 4 THEN OrderSize END MediumOrderProducts,
CASE WHEN OrderSize BETWEEN 3 AND 4 THEN OrderTotal END MediumOrderTotal,
CASE WHEN OrderSize > 4 THEN OrderSize END LargeOrderProducts,
CASE WHEN OrderSize > 4 THEN OrderTotal END LargeOrderTotal
FROM Orders
)
SELECT CustomerID,
COUNT(SmallOrderProducts) SmallOrderCount,
COALESCE(SUM(SmallOrderProducts), 0) SmallOrderProducts,
COALESCE(SUM(SmallOrderTotal), 0) SmallOrderTotal,
COUNT(MediumOrderProducts) MediumOrderCount,
COALESCE(SUM(MediumOrderProducts), 0) MediumOrderProducts,
COALESCE(SUM(MediumOrderTotal), 0) MediumOrderTotal,
COUNT(LargeOrderProducts) LargeOrderCount,
COALESCE(SUM(LargeOrderProducts), 0) LargeOrderProducts,
COALESCE(SUM(LargeOrderTotal), 0) LargeOrderTotal
FROM cteOrders
GROUP BY CustomerID
You can do this by using single INSERT INTO SELECT statement
INSERT INTO Customers
SELECT * ,
(SELECT count(*) FROM Orders WHERE OrderSize BETWEEN 1 AND 2 AND Orders.CustomerID = CustomerID),
(SELECT sum(OrderSize) FROM Orders WHERE OrderSize BETWEEN 1 AND 2 AND Orders.CustomerID = CustomerID)
FROM CUSTOMERS

Resources