COUNT & PRINT matched Row values in NEW COLUMNS - sql

I am trying to make the column name from row value and count the total value of each row name to show into the particular column name. Result will show from 30 days.
SELECT id
, Name
, Designation
, DeptName
, Sts
, COUNT(Sts) as 'COUNT'
FROM table
WHERE Date >= DATEADD(month,-1,GETDATE())
AND Name = 'Neo'
GROUP BY id, Name, Designation, DeptName, Sts
ORDER id
Here is my output
Required Output is...

use conditional aggregation
SELECT id
, Name
, Designation
, DeptName
, sum (case when Sts='H' then 1 end) H
,sum (case when Sts='A' then 1 end) A
,sum (case when Sts='P' then 1 end) P
FROM table
WHERE Date >= DATEADD(month,-1,GETDATE())
AND Name = 'Neo'
GROUP BY id, Name, Designation, DeptName
ORDER id
The CASE statement goes through conditions and return a value when the first condition is met (like an IF-THEN-ELSE statement) and when a condition will meet it will sum up for that particular value

As eariler suggested, use pivot
WITH temp AS(
SELECT 1 AS id,'Neo' AS name,'Zonal Manager' AS designation, 'Admin' AS deptname, 'H' AS sts ,2 AS cnt FROM dual UNION ALL
SELECT 1,'Neo','Zonal Manager', 'Admin', 'A',3 FROM dual UNION ALL
SELECT 1,'Neo','Zonal Manager', 'Admin', 'P',4 FROM dual UNION ALL
SELECT 1,'Neo','Zonal Manager', 'Admin', 'A',1 FROM dual UNION ALL
SELECT 1,'Neo','Zonal Manager', 'Admin', 'A',8 FROM dual )
SELECT p.*
FROM temp
PIVOT(
SUM(cnt)
FOR sts
IN ('A', 'P', 'H')
) p

Related

How to compare different values within the same column

I having two tables emp and type.
create table EMP(ID number(10), effective_date date);
EID Effective_date
--------------------
1 02/14/2023
2 02/15/2023
3 04/30/2023
4 03/24/2023
create table type(ID number(10),contract_type varchar2(2));
TID contract_type
------------------
1 P
1 S
1 P
2 S
2 S
3 P
3 S
4 S
I am looking EID which is having contract type is 'S' in type table. (or emp table with effective date is greater than sysdate and in the type table with only contract_type ='S')
Actual result :
2
4
My query is not giving the correct results.
select emp.EID
from emp,type
where EID = TID
contract_type ='S'
effective_date >= sysdate
group by TID
having count(TID) >= 1;
If you want to keep your idea with COUNT and GROUP BY, you should count other contract types than the 'S' ones and check this is 0:
SELECT e.eid
FROM emp e
JOIN type t ON e.eid = t.tid
WHERE
e.effective_date >= sysdate
GROUP BY e.eid
HAVING COUNT(CASE WHEN t.contract_type <> 'S' THEN 1 END) = 0;
This query will return 2 and 4 for your sample data.
Try out: db<>fiddle
Another option is as already said here using NOT EXISTS.
Take care of following difference to the NOT EXISTS approach: The query in Tim's answer will also fetch id's of table "emp" that don't appear at all in table "type". My query here will not fetch such id's.
It's up to you to decide whether this is possible at all and what to do in this case.
Changing JOIN to LEFT JOIN in above query will eliminate this difference.
I would use exists logic here:
SELECT EID
FROM EMP e
WHERE effective_date >= SYSDATE AND
NOT EXISTS (
SELECT 1
FROM "type" t
WHERE t.TID = e.EID AND
t.contract_type <> 'S'
);
You could use Count() Over() analytic function to check for type 'S' and number of different types per ID.
SELECT DISTINCT ID
FROM ( Select e.EID "ID",
Count(CASE t.CONTRACT_TYPE WHEN 'S' THEN 'S' END) Over(Partition By t.ID Order By t.ID) "NUM_OF_S",
Count(Distinct t.CONTRACT_TYPE) Over(Partition By t.ID) "NUM_OF_TYPES",
TRUNC(e.EFFECTIVE_DATE) - TRUNC(SYSDATE) "DAYS_AFTER_SYSDATE"
From emp_cte e
Inner Join type_cte t ON(t.ID = e.EID) )
WHERE NUM_OF_S > 0 And -- Type 'S' exists for ID AND
NUM_OF_TYPES = 1 And -- It is the only type AND
DAYS_AFTER_SYSDATE > 0 -- EFFECTIVE_DATE is after SYSDATE
With your sample data ...
WITH
emp_cte(EID, EFFECTIVE_DATE) AS
(
Select 1, To_Date('02/14/2023', 'mm/dd/yyyy') From Dual Union All
Select 2, To_Date('02/15/2023', 'mm/dd/yyyy') From Dual Union All
Select 3, To_Date('04/30/2023', 'mm/dd/yyyy') From Dual Union All
Select 4, To_Date('03/24/2023', 'mm/dd/yyyy') From Dual
),
type_cte(ID, CONTRACT_TYPE) AS
(
Select 1, 'P' From Dual Union All
Select 1, 'S' From Dual Union All
Select 1, 'P' From Dual Union All
Select 2, 'S' From Dual Union All
Select 2, 'S' From Dual Union All
Select 3, 'P' From Dual Union All
Select 3, 'S' From Dual Union All
Select 4, 'S' From Dual
)
... result would be ...
-- ID
-- ----------
-- 2
-- 4

Selection of rows providing a specific condition in sql

I want to create a query like this:
For student_name, if the number of grade=0 and grade=1 students is equal, let's not select this person, but if the number of grade=0 and grade=1 students is different, let's select this person. In the query I will use for my example, Jack will not be selected, everyone else will be selected.
CREATE TABLE student
(
student_name VARCHAR(50),
grade CHAR(1)
)
INSERT INTO student
SELECT 'Jack', '0' UNION ALL
SELECT 'Jack', '0' UNION ALL
SELECT 'Jack', '0' UNION ALL
SELECT 'Jack', '1' UNION ALL
SELECT 'Jack', '1' UNION ALL
SELECT 'Jack', '1' UNION ALL
SELECT 'Rose', '0' UNION ALL
SELECT 'Rose', '0' UNION ALL
SELECT 'John', '1' UNION ALL
SELECT 'John', '1' UNION ALL
SELECT 'John', '1' UNION ALL
SELECT 'John', '1' UNION ALL
SELECT 'Dave', '1' UNION ALL
SELECT 'Dave', '1' UNION ALL
SELECT 'Chris', '0'
select * from student
Use aggregation and set the condition in the HAVING clause:
SELECT student_name
FROM student
GROUP BY student_name
HAVING COUNT(CASE WHEN grade = '0' THEN 1 END) <> COUNT(CASE WHEN grade = '1' THEN 1 END);
See the demo.

SQL: Increment ID only for new rows based on the count

Requirement: Generate new ID from the MAX ID for those Name doesn't exist in the Target table and has count >1
Below is the Source data, The yellow highlighted are new rows, Those with count >1 are incremented with a new ID, and those with count =1 defaults to FM00000001
The expected result is highlighted in yellow in the Target table
I have generated the existing ID manually for one time , as I have to automate daily jobs so I need to generate incremental ID from MAX ID for those count >1
with src as (
select '121 MEDICAL PLACE' as Label , 1 as cnt
union all
select '16TH STREET COMMUNITY' as Label , 1 as cnt
union all
select '19TH AVENUE CLINIC' as Label , 2 as cnt
union all
select '1ST CLASS URGENT CARE' as Label , 3 as cnt
union all
select '160 DORADO BCH' as Label , 2 as cnt
union all
select 'APPLETREE LN' as Label , 4 as cnt
union all
select 'KNOLLWOOD LN' as Label , 1 as cnt
)
select * from src
with tgt as (
select '121 MEDICAL PLACE' as Label , 'FM00000001' as ID
union all
select '16TH STREET COMMUNITY' as Label , 'FM00000001'as ID
union all
select '19TH AVENUE CLINIC' as Label , 'FM00000002'as ID
union all
select '1ST CLASS URGENT CARE' as Label , 'FM00000003' as ID
)
select * from tgt
ok If I understand correctly , here is how you can do it :
select *
, 'FM'+ LPAD(case when cnt =1 then cnt else count(case when cnt > 1 then 1 end) over (order by OrderColumn ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) + 1 end, 8,0) as ID
from table
for reference: Window Functions

T-SQL to stamp individual rows

I'm looking for some T-SQL code that will add a column called tag which will tag each row with the same number until there is a change in value within any of the columns "team", "id", "kmvid", "name", "cid" and "pid". If there is a change, use the next sequence of numbering for that row. See the expected results below as image.
You can do this with cumulative sums and lag():
select t.*,
sum(case when prev_team = team and
id = id and
kmvid = kmvid and
name = name and
prev_id = id and
prev_cid = pid and
prev_oid = cid
then 1 else 0
end) over (order by date) as Tag
from (select t.*,
lag(team) over (order by date) as prev_team,
lag(id) over (order by date) as prev_id,
lag(kmvid) over (order by date) as prev_kmvid,
lag(name) over (order by date) as prev_name,
lag(cid) over (order by date) as prev_cid,
lag(pid) over (order by date) as prev_pid
from t
) t;
You should not use a cursor for things that are better done using set-based operations.
The following will give you the desired results based on the data you took a picture of...
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
CREATE TABLE #TestData (
CountryId CHAR(2) NOT NULL,
[Date] DATETIME NOT NULL
);
INSERT #TestData (CountryId, Date)
SELECT '99', '2004-04-30' UNION ALL
SELECT '99', '2004-07-31' UNION ALL
SELECT '99', '2004-10-31' UNION ALL
SELECT '99', '2005-01-31' UNION ALL
SELECT '99', '2005-04-30' UNION ALL
SELECT '99', '2005-07-31' UNION ALL
SELECT '99', '2005-10-31' UNION ALL
SELECT '99', '2006-01-31' UNION ALL
SELECT '99', '2006-04-30' UNION ALL
SELECT '99', '2006-07-31' UNION ALL
SELECT '99', '2006-10-31' UNION ALL
SELECT '99', '2007-01-31' UNION ALL
SELECT 'HK', '2007-04-30' UNION ALL
SELECT 'CA', '2007-07-31' UNION ALL
SELECT 'HK', '2007-10-31';
-- SELECT * FROM #TestData td;
--=======================================
WITH
cte_TagGroup AS (
SELECT
td.CountryId,
td.[Date],
TagGroup = ROW_NUMBER() OVER (ORDER BY td.Date)
- ROW_NUMBER() OVER (PARTITION BY td.CountryId ORDER BY td.Date)
- CASE WHEN td.CountryId = LAG(td.CountryId, 1, td.CountryId) OVER (ORDER BY td.CountryId, td.Date) THEN 0 ELSE 1 END
FROM
#TestData td
)
SELECT
tg.CountryId,
tg.Date,
Tag = DENSE_RANK() OVER (ORDER BY tg.TagGroup)
FROM
cte_TagGroup tg;
... That said, I suspect that the 1st 3 columns of data aren't all the same values, so you may need to add columns to the "PARTITION BY" clauses to make it fit your actual data.
HTH,
Jason

Query for Multiple row SQL Where clause

Need help for one query which is fetching result from multiple rows based on some condition. For e.g. we have table with [Roll no] with [subjects]. Table can have multiple records for the same [Roll No]. My requirement is if the Student opt for only 'English' then result should return 'E', if Maths then 'M' and if both then 'B'.
// I think this is what you want.
INSERT INTO dbo.rolls
( name, subject )
VALUES ( 'Jones', 'English'),
( 'Smith', 'Math'),
('Adams','English'),
('Adams', 'Math')
GO
;WITH CTE AS (
SELECT subquery1.name, 'B' AS code FROM (
SELECT name,COUNT(name) AS cnt
FROM rolls
WHERE subject = 'English' OR subject = 'Math'
GROUP BY name
HAVING COUNT(name) > 1 ) AS subquery1
UNION
SELECT subquery2.name, SUBSTRING(rolls.subject,1,1) AS code FROM (
SELECT name,COUNT(name) AS cnt
FROM rolls
WHERE subject = 'English' OR subject = 'Math'
GROUP BY name
HAVING COUNT(name) = 1 ) AS subquery2
INNER JOIN dbo.rolls
ON rolls.name = subquery2.name
)
SELECT * FROM CTE