How to compare column data with entire table data - sql

I have sample result set :
declare #Emp Table (Emp_Name VARCHAR(10),Dept_name VARCHAR(10),Subjects VARCHAR(10),Score VARCHAR(10))
Insert into #EMP(Emp_Name,Dept_name,Subjects,Score)VALUES ('MOHAN','LANGUAGES','English',35)
Insert into #EMP(Emp_Name,Dept_name,Subjects,Score)VALUES ('MOHAN','LANGUAGES','TELUGU',35)
Insert into #EMP(Emp_Name,Dept_name,Subjects,Score)VALUES ('MOHAN','SCIENCE','BIOLOGY',35)
Insert into #EMP(Emp_Name,Dept_name,Subjects,Score)VALUES ('MOHAN','SCIENCE','PHYSICS',35)
Insert into #EMP(Emp_Name,Dept_name,Subjects,Score)VALUES ('MOHAN','SOCIAL','ECONOMICS',35)
Insert into #EMP(Emp_Name,Dept_name,Subjects,Score)VALUES ('MOHAN','SOCIAL','CIVICS',35)
Insert into #EMP(Emp_Name,Dept_name,Subjects,Score)VALUES ('MOHAN','SOCIAL','ECONOMICS',35)
Sample Data :
Emp_Name Dept_name Subjects Score
MOHAN LANGUAGES English 35
MOHAN LANGUAGES TELUGU 35
MOHAN SCIENCE BIOLOGY 35
MOHAN SCIENCE PHYSICS 35
MOHAN SOCIAL ECONOMICS 35
MOHAN SOCIAL CIVICS 35
MOHAN SOCIAL ECONOMICS 35
Need to compare entire table data with each column data :
Select COUNT(*) ALL_COl FROM (
select Emp_Name,Dept_name,Subjects,Score from #Emp )T
Select Count(*) Without_Subject_Col FROM (
select DISTINCT Emp_Name,Dept_name,Score from #Emp)TT
Select count(*) Without_Dept_Col from (
select DISTINCT Emp_Name,Subjects,Score from #Emp )TTT
Select COUNT(*) Without_Dept_Subject_Col from (
select DISTINCT Emp_Name,Score from #Emp )TTTT
How can I get the output like this :
ALL_COL Without_Subject_Col Without_Dept_Col Without_Dept_Subject_Col
7 3 6 1
Suggest me the way how to achieve it

You could just OUTER APPLY the counts?
select top 1 a.ALL_COl,
aa.Without_Subject_Col,
aaa.Without_Dept_Col,
aaaa.Without_Dept_Subject_Col
from #EMP e
outer apply
(Select COUNT(*) ALL_COl FROM (
select Emp_Name,Dept_name,Subjects,Score from #Emp )T ) A
outer apply
(Select Count(*) Without_Subject_Col FROM (
select DISTINCT Emp_Name,Dept_name,Score from #Emp)TT ) AA
outer apply
(Select count(*) Without_Dept_Col from (
select DISTINCT Emp_Name,Subjects,Score from #Emp )TTT)AAA
outer apply
(Select COUNT(*) Without_Dept_Subject_Col from (
select DISTINCT Emp_Name,Score from #Emp )TTTT)AAAA

Just select the counts as sub-queries
SELECT
(
SELECT COUNT(*) FROM #Emp
) AS ALL_Col,
(
SELECT COUNT(*)
FROM (SELECT DISTINCT Emp_Name, Dept_name, Score FROM #Emp) q
) AS Without_Subject_Col,
(
SELECT COUNT(*)
FROM (SELECT DISTINCT Emp_Name, Subjects, Score FROM #Emp) q
) AS Without_Dept_Col,
(
SELECT COUNT(*)
FROM (SELECT DISTINCT Emp_Name, Score FROM #Emp) q
) AS Without_Dept_Subject_Col;
A test on rextester here

No idea why you want this but using count with distinct makes this pretty simple.
Select ALL_COL = COUNT(*)
, Without_Subject_Col = Count(DISTINCT Emp_Name + Dept_name + convert(varchar(5), Score))
, Without_Dept_Col = count(distinct Emp_Name + Subjects + convert(varchar(5), Score))
, Without_Dept_Subject_Col = count(distinct Emp_Name + convert(varchar(5), Score))
from #Emp

Related

get records that have only 1 record per group

we have attendance db data as follows (sql server)
empid date type
1 01-Jan In
1 01-Jan Out
2 01-Jan In
3 01-Jan In
3 01-Jan Out
How can we get records that have only 1 record per date per employee (in above case empid 2 for 01-jan)?
The query should simply list all records of employees that have only single type for a day.
EDIT
The result set should be a bit more specific: show all employee who only have "In" for a date but no "Out"
Use Having
select empid, date, count(*)
from Mytable
group by empid, date
having count(*) = 1
You can use this to get the full line:
select t1.*
from MyTable t1
inner join
(
select empid, date, count(*)
from Mytable
group by empid, date
having count(*) = 1
) t2
on t1.empid = t2.empid
and t1.date = t2.date
You can use window functions:
select t.*
from (select t.*,
count(*) over (partition by empid, date) as cnt
from t
) t
where cnt = 1;
You can also use aggregation:
select empid, date, max(type) as type
from t
group by empid, date
having count(*) = 1;
Use a correlated subquery
select * from tablename a
where not exists (select 1 from tablename b where a.empid=b.empid and a.date=b.date and type='Out')
OR
select empid, date,count(distinct type)
from tablename
group by empid,date
having count(distinct type)=1
The Solution is Very Simple, You can use 'DISTINCT' function.
Query Should be as,
SELECT DISTINCT empid FROM attendance
This will return only 1 record per date per employee.
For Your Reference, Check it out- https://www.techonthenet.com/sql_server/distinct.php
This will work if we have ID with 1 IN OR 1 OUT as well
Declare #t table (empid int,date varchar(50),types varchar(50))
insert into #t values (1,'01-Jan','IN')
insert into #t values (1,'01-Jan','OUT')
insert into #t values (2,'01-Jan','IN')
insert into #t values (3,'01-Jan','OUT')
insert into #t values (4,'01-Jan','OUT')
select * from #t a
where not exists (select 1 from #t b where a.empid=b.empid and a.types!=b.types)

How to retrieve Count of all records and other records in single query

I have the following query
select name,trip_id from main order by name
I want to retrieve count of all the records and all the columns in the tables.
for ex if i have 200 rows in table i want to have the output as
select name,trip_id,count(*) from main
Is it possible in a single query?
Use analytic count:
select name, trip_id,
count(*) over() as cnt
from main
order by name
;
DECLARE
#Main TABLE
(
[name] VARCHAR(50),
trip_id INT
);
INSERT INTO #Main
(
[name],
trip_id
)
VALUES
(
'Jim', 1
),
(
'Ian', 2
),
(
'Susan', 2
);
-- Option 1
SELECT
[name],
trip_id,
COUNT(*) OVER () AS ct
FROM
#Main;
-- Option 2
SELECT
[name],
trip_id,
(
SELECT
COUNT(*)
FROM
#Main
)
ct
FROM
#Main;
-- Option 3
SELECT
[name],
trip_id,
v.ct
FROM
#Main
CROSS APPLY
(
SELECT
COUNT(*) FROM #Main
) v (ct);
-- Option 4
SELECT
t1.[name],
t1.trip_id,
t2.ct
FROM
#Main t1
JOIN
(
SELECT
COUNT(*) ct
FROM
#Main
) t2
ON 1 = 1;
-- Option 5
SELECT
t1.[name],
t1.trip_id,
t2.ct
FROM
#Main t1
CROSS JOIN
(
SELECT
COUNT(*) ct
FROM
#Main
) t2;

Can I delete multiple of nth rows in a single query from a table in SQL?

I want to delete multiple of 4 from my table which have thousands of record. How can I do it?
Ex:-
Table1
1 a
2 b
3 c
4 d
5 e
6 f
7 g
8 h
9 i
I want to delete every 4th row.
I don't want to use a loop or cursor.
Delete A from
(
Select *,Row_Number() Over(Order By Id) as RN from TableA
) A
where RN%4=0
SQL Fiddle Link
Try this...
delete from table_name where (col1 % 4) = 0
Use a CTE.
WITH cte AS (
SELECT t.*, ROW_NUMBER() OVER (ORDER BY t.rowfield) AS rank
FROM Table1 t)
SELECT rowfield, fielda
FROM cte
WHERE rank%4 != 0
Output
rowfield fielda
1 a
2 b
3 c
5 e
6 f
7 g
9 i
SQL Fiddle: http://sqlfiddle.com/#!6/c9540b/13/0
Once you are happy with the output use DELETE FROM.
This can use an index if one exists and uses numbers table
;with cte
as
(select n from numbers
where n%4=0
)
delete t
from table1 t
join
cte c
on c.n=t.id
Try this
DECLARE #nvalToDelete varchar(100)='4,7,3,8,9'-- just give values to delete from table
DECLARE #temp TABLE
(
valtodelete VARCHAR(100)
)
DECLARE #Deletetemp TABLE
(
valtodelete INT
)
INSERT INTO #temp
SELECT #nvalToDelete
INSERT INTO #Deletetemp
SELECT split.a.value('.', 'VARCHAR(1000)') AS valToDelete
FROM (SELECT Cast('<S>' + Replace(valtodelete, ',', '</S><S>')
+ '</S>' AS XML) AS valToDelete
FROM #temp) AS A
CROSS apply valtodelete.nodes('/S') AS Split(a)
DECLARE #Table1 TABLE
(
ID INT,
val varchar(10)
)
INSERT INTO #Table1
SELECT 1,'a' UNION ALL
SELECT 2,'b' UNION ALL
SELECT 3,'c' UNION ALL
SELECT 4,'d' UNION ALL
SELECT 5,'e' UNION ALL
SELECT 6,'f' UNION ALL
SELECT 7,'g' UNION ALL
SELECT 8,'h' UNION ALL
SELECT 9,'i'
SELECT *
FROM #Table1;
WITH cte
AS (SELECT *,
RN = Row_number()
OVER (
ORDER BY id )
FROM #Table1)
DELETE FROM #Table1
WHERE id IN(SELECT id FROM cte
WHERE rn IN (SELECT CASt(valToDelete AS INT) FROM #Deletetemp)
)
SELECT *
FROM #Table1

SQL:How do i get the row that has the max value of a column in SQL Server

I have a data a set which is already grouped by Person and Class columns and I use this query for this process:
SELECT Person,Class, MAX(TimeSpent) as MaxTimeSpent
FROM Persons
GROUP BY Person,Class
Output:
Person Class MaxTimeSpent
--------|--------|-------------|
MJ | 0 | 0 |
MJ | 1 | 659 |
MJ | 2 | 515 |
What I want to do is to get the row that has the maximum Class value in this data set (which is the 3rd row for this example).
How can I do this ? Any help would be appreciated.
Try this one
SELECT T.*
FROM
(SELECT Person,
Class,
MAX(TimeSpent) AS MaxTimeSpent
FROM Persons AS P
WHERE Person = 'MJ'
GROUP BY Person, Class) AS T
WHERE T.class = (
SELECT MAX(class) FROM Persons AS P
WHERE P.person = T.person)
You can use cte for that.
declare #Persons table (person nvarchar(10),Class int ,TimeSpent int)
insert into #Persons
select 'MJ',0,0 union all
select 'MJ',1,659 union all
select 'MJ',2,515
;with cte
as(
SELECT Person,Class,TimeSpent , row_number() over(partition by Person order by Class desc ) as RN
FROM #Persons
)
select * from cte where RN=1
Solution 2 :With Out Cte:
SELECT * FROM (
SELECT Person
,Class
,TimeSpent
,row_number() OVER (PARTITION BY Person ORDER BY Class DESC) AS RN FROM #Persons
) t WHERE t.RN = 1
TRY This:
declare #t table (Person varchar (20),Class int ,MaxTimeSpent int )
insert into #t VALUES ('MJ',0,0)
insert into #t VALUES ('MJ',1,659)
insert into #t VALUES ('MJ',2,515)
SELECT TOP 1 * FROM #t ORDER BY 2 DESC
--OR
SELECT * FROM #t WHERE Class = (SELECT max(class) FROM #t)
-- OR
SELECT TOP 1 * FROM (
SELECT *
,ROW_NUMBER() OVER (PARTITION BY person ORDER BY Class DESC) Record_Count FROM #t
) a
SELECT Top 1 Person,Class, MAX(TimeSpent) as MaxTimeSpent
FROM Persons
GROUP BY Person,Class order by Class desc

How to select top 3 values from each group in a table with SQL which have duplicates [duplicate]

This question already has answers here:
Select top 10 records for each category
(14 answers)
Closed 5 years ago.
Assume we have a table which has two columns, one column contains the names of some people and the other column contains some values related to each person. One person can have more than one value. Each value has a numeric type. The question is we want to select the top 3 values for each person from the table. If one person has less than 3 values, we select all the values for that person.
The issue can be solved if there are no duplicates in the table by the query provided in this article Select top 3 values from each group in a table with SQL . But if there are duplicates, what is the solution?
For example, if for one name John, he has 5 values related to him. They are 20,7,7,7,4. I need to return the name/value pairs as below order by value descending for each name:
-----------+-------+
| name | value |
-----------+-------+
| John | 20 |
| John | 7 |
| John | 7 |
-----------+-------+
Only 3 rows should be returned for John even though there are three 7s for John.
In many modern DBMS (e.g. Postgres, Oracle, SQL-Server, DB2 and many others), the following will work just fine. It uses CTEs and ranking function ROW_NUMBER() which is part of the latest SQL standard:
WITH cte AS
( SELECT name, value,
ROW_NUMBER() OVER (PARTITION BY name
ORDER BY value DESC
)
AS rn
FROM t
)
SELECT name, value, rn
FROM cte
WHERE rn <= 3
ORDER BY name, rn ;
Without CTE, only ROW_NUMBER():
SELECT name, value, rn
FROM
( SELECT name, value,
ROW_NUMBER() OVER (PARTITION BY name
ORDER BY value DESC
)
AS rn
FROM t
) tmp
WHERE rn <= 3
ORDER BY name, rn ;
Tested in:
Postgres
Oracle
SQL-Server
In MySQL and other DBMS that do not have ranking functions, one has to use either derived tables, correlated subqueries or self-joins with GROUP BY.
The (tid) is assumed to be the primary key of the table:
SELECT t.tid, t.name, t.value, -- self join and GROUP BY
COUNT(*) AS rn
FROM t
JOIN t AS t2
ON t2.name = t.name
AND ( t2.value > t.value
OR t2.value = t.value
AND t2.tid <= t.tid
)
GROUP BY t.tid, t.name, t.value
HAVING COUNT(*) <= 3
ORDER BY name, rn ;
SELECT t.tid, t.name, t.value, rn
FROM
( SELECT t.tid, t.name, t.value,
( SELECT COUNT(*) -- inline, correlated subquery
FROM t AS t2
WHERE t2.name = t.name
AND ( t2.value > t.value
OR t2.value = t.value
AND t2.tid <= t.tid
)
) AS rn
FROM t
) AS t
WHERE rn <= 3
ORDER BY name, rn ;
Tested in MySQL
I was going to downvote the question. However, I realized that it might really be asking for a cross-database solution.
Assuming you are looking for a database independent way to do this, the only way I can think of uses correlated subqueries (or non-equijoins). Here is an example:
select distinct t.personid, val, rank
from (select t.*,
(select COUNT(distinct val) from t t2 where t2.personid = t.personid and t2.val >= t.val
) as rank
from t
) t
where rank in (1, 2, 3)
However, each database that you mention (and I note, Hadoop is not a database) has a better way of doing this. Unfortunately, none of them are standard SQL.
Here is an example of it working in SQL Server:
with t as (
select 1 as personid, 5 as val union all
select 1 as personid, 6 as val union all
select 1 as personid, 6 as val union all
select 1 as personid, 7 as val union all
select 1 as personid, 8 as val
)
select distinct t.personid, val, rank
from (select t.*,
(select COUNT(distinct val) from t t2 where t2.personid = t.personid and t2.val >= t.val
) as rank
from t
) t
where rank in (1, 2, 3);
Using GROUP_CONCAT and FIND_IN_SET you can do that.Check SQLFIDDLE.
SELECT *
FROM tbl t
WHERE FIND_IN_SET(t.value,(SELECT
SUBSTRING_INDEX(GROUP_CONCAT(t1.value ORDER BY VALUE DESC),',',3)
FROM tbl t1
WHERE t1.name = t.name
GROUP BY t1.name)) > 0
ORDER BY t.name,t.value desc
If your result set is not so heavy, you can write a stored procedure (or an anonymous PL/SQL-block) for that problem which iterates the result set and finds the bigges three by a simple comparing algorithm.
Try this -
CREATE TABLE #list ([name] [varchar](100) NOT NULL, [value] [int] NOT NULL)
INSERT INTO #list VALUES ('John', 20), ('John', 7), ('John', 7), ('John', 7), ('John', 4);
WITH cte
AS (
SELECT NAME
,value
,ROW_NUMBER() OVER (
PARTITION BY NAME ORDER BY (value) DESC
) RN
FROM #list
)
SELECT NAME
,value
FROM cte
WHERE RN < 4
ORDER BY value DESC
This works for MS SQL. Should be workable in any other SQL dialect that has the ability to assign row numbers in a group by or over clause (or equivelant)
if object_id('tempdb..#Data') is not null drop table #Data;
GO
create table #data (name varchar(25), value integer);
GO
set nocount on;
insert into #data values ('John', 20);
insert into #data values ('John', 7);
insert into #data values ('John', 7);
insert into #data values ('John', 7);
insert into #data values ('John', 5);
insert into #data values ('Jack', 5);
insert into #data values ('Jane', 30);
insert into #data values ('Jane', 21);
insert into #data values ('John', 5);
insert into #data values ('John', -1);
insert into #data values ('John', -1);
insert into #data values ('Jane', 18);
set nocount off;
GO
with D as (
SELECT
name
,Value
,row_number() over (partition by name order by value desc) rn
From
#Data
)
SELECT Name, Value
FROM D
WHERE RN <= 3
order by Name, Value Desc
Name Value
Jack 5
Jane 30
Jane 21
Jane 18
John 20
John 7
John 7