Subtract Two columns off the condition of two other column values - sql

Having a lot of trouble thinking through this problem, but please view below example table:
CREATE TABLE IF NOT EXISTS `table_1`
(
`num` float NOT NULL,
`first_name` varchar(200) NOT NULL,
`last_name` varchar(200) NOT NULL,
PRIMARY KEY (`date`)
);
INSERT INTO `table_1` (`num`, `first_name`, `last_name`)
VALUES ('1', 'John', 'Wonk'),
('5', 'John', 'Woo'),
('3', 'James', 'Lee');
This is the output I want: "If first_name equals first_name and last_name does not equal last_name then subtract num with last_name = 'Woo' from num" So the result it would have 1 - 5 (using table data below) and the output would be -4

A Simple Left join on first_name equal and last_name not equal will align the num field with a value, when matched, or null. You cast null's to 0 and do your math for each records so that those that meet the criteria will adjust.
SELECT
D1.first_name,
D1.last_name,
num = D1.num - ISNULL(D2.num,0)
FROM
table_1 D1
LEFT OUTER JOIN table_1 D2 ON D2.first_name = D1.first_name AND D1.last_name <> D2.last_name

Is this what you want?
select min(num) - max(num)
from t
group by first_name
having min(last_name) <> max(last_name);

Related

Select rows after the current row which are null and combine them to rows

I have written the following SQL code to display the data as rows, where the row after data is having null values except on the description column.
DECLARE #StudentData TABLE
(
RowID INT NOT NULL PRIMARY KEY IDENTITY(1,1),
RemarksDate NVARCHAR(20),
StudentName NVARCHAR(1000),
Description NVARCHAR(MAX),
TotStudents NVARCHAR(100)
)
INSERT INTO #StudentData(RemarksDate, StudentName, Description, TotStudents)
VALUES('2/1/2021', NULL, 'Poor In English', '14'),
(NULL, NULL, '1 ABC', NULL),
(NULL, NULL, '1 XYZ', NULL),
(NULL, NULL, '1 MNO', NULL),
(NULL, NULL, '1 IGH', NULL),
(NULL, NULL, '10 KKK', NULL),
('2/1/2021', NULL, 'Poor In Maths', '5'),
(NULL, NULL, '5 PQR', NULL),
('2/8/2021', NULL, 'Poor In Social', '1'),
(NULL, NULL, '1 RST', NULL)
This results in the output as follows:
I have written the following query to group and display rows:
SELECT t1.RemarksDate, LTRIM(RIGHT(t2.Description, LEN(t2.Description) - PATINDEX('%[0-9][^0- 9]%', t2.Description ))) StudentName, t1.Description
,LEFT(t2.Description, PATINDEX('%[0-9][^0-9]%', t2.Description ))
FROM (
SELECT *, RowID + TotStudents MaxVal
FROM #StudentData
WHERE RemarksDate is NOT NULL
) t1
JOIN (
SELECT *
FROM #StudentData
WHERE RemarksDate is NULL
) t2 ON t2.RowId BETWEEN t1.RowID and t1.MaxVal
The data is displayed as follows
Expected output is as follows
2/1/2021 ABC Poor In English 1
2/1/2021 XYZ Poor In English 1
2/1/2021 MNO Poor In English 1
2/1/2021 IGH Poor In English 1
2/1/2021 KKK Poor In English 10
2/1/2021 PQR Poor In Maths 5
2/8/2021 RST Poor In Social 1
This is a type of gaps-and-islands problem. There are many solutions, I will give you one that only requires a single scan of the base table.
We have a header row and child rows, and we need to apply the header row values to the child rows.
We can solve this by defining the start point of each group, then taking windowed header values for each group and finally filtering out the header rows
WITH Groupings AS (
SELECT *,
GroupId = MAX(CASE WHEN Description LIKE 'Poor%' THEN RowID END)
OVER (ORDER BY RowID ROWS UNBOUNDED PRECEDING)
FROM #StudentData s
),
GroupValues AS (
SELECT
RemarksDate = MAX(CASE WHEN Description LIKE 'Poor%' THEN RemarksDate END)
OVER (PARTITION BY GroupId),
DescriptionHeader = MAX(CASE WHEN Description LIKE 'Poor%' THEN Description END)
OVER (PARTITION BY GroupId),
Space = CHARINDEX(' ', Description),
Description
FROM Groupings
)
SELECT
RemarksDate,
DescriptionHeader,
StudentName = SUBSTRING(Description, Space + 1, LEN(Description)),
SomeNumber = LEFT(Description, Space - 1)
FROM GroupValues
WHERE Description NOT LIKE 'Poor%';
db<>fiddle
Except the fact that the table design is pretty awful, I would suggest the following approach:
WITH cteRemarks AS(
SELECT *, LEAD(RowId) OVER (ORDER BY RowID) AS RowIdNxt
FROM #StudentData
WHERE TotStudents IS NOT NULL
)
SELECT r.RemarksDate
,RIGHT(t.Description, LEN(t.Description)-CHARINDEX(' ', t.Description)) AS StudentsName
,r.Description AS Description
,LEFT(t.Description, CHARINDEX(' ', t.Description)-1) AS Val
FROM cteRemarks r
LEFT JOIN #StudentData t ON t.TotStudents IS NULL
AND t.RowID > r.RowID
AND t.RowID < ISNULL(r.RowIDNxt, 99999999)

SQL Self Join Update

I need to procedurally update a table by self-joining itself. Using SQL Server 2019.
CREATE TABLE Sect
(
Section_Id INT,
Locale VARCHAR(10),
Record_Id INT,
Section_Id_1 INT
);
INSERT INTO Sect (Section_Id, Locale, Record_Id, Section_Id_1)
VALUES
(100, 'US', 1, Null),
(101, 'CA', Null, 100),
(101, 'MD', Null, 100)
The goal is to update the null values of Record_Id where Section_Id_1 equals Section_ID with the matching Record_ID.
This is the intended result:
100|US|1|Null
101|CA|1|100
101|MD|1|100
I think I am close with:
UPDATE t1
SET Record_Id = t2.Record_Id
FROM Sect t1
INNER JOIN Sect t2 ON t1.Section_Id_1 = t2.Section_Id
WHERE t1.Record_Id IS NULL
Appreciate your all's help.
I'm not familiar with SQL Server 2019. But I think you don't need to use INNER JOIN, try this.
update
Sect t1
set
Record_Id = (select t2.Record_Id
from Sect t2
where t2.Section_ID = t1.Section_Id_1
and t2.Record_Id is not null
limit 1)
where Record_Id is null
and Section_Id_1 is not null

SQL select statement headings

Having trouble working out how to do headings
Display the total number of FEMALE students under the heading 'FEMALE STUDENTS':
Struture:
CREATE TABLE Student
(StudID INTEGER PRIMARY KEY,
StudFName VARCHAR(10) NOT NULL,
StudLName VARCHAR(10) NOT NULL,
DoB DATE NOT NULL,
Sex CHAR(1) NOT NULL CHECK (Sex IN ('M', 'F')),
Email VARCHAR(30) UNIQUE);
SELECT count(Sex) as "FEMALE STUDENTS"
from Student
where sex='F';
You might also try the following:
SELECT COUNT(*) AS "Total Students"
, SUM(DECODE(sex, 'F', 1, 0)) AS "Female Students"
, SUM(DECODE(sex, 'F', 0, 1)) AS "Male Students"
FROM students
I use this method often to get multiple counts as part of one statement.
Use count() function to retrieve the number of rows under a particular column.
Syntax:
SELECT COUNT(column_name) FROM table_name;
In your case the query would be:
SELECT count(sex) from Student where sex='F';
COUNT returns the number of rows returned by the query. You can use it as an aggregate or analytic function.
For more info:
http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions032.htm
http://www.w3schools.com/sql/sql_func_count.asp

Retrieve records through inner join

I have a schema as per the below:
CREATE TABLE rruser (
id NUMBER(32,0) NOT NULL,
name VARCHAR2(30) NOT NULL,
fullname VARCHAR2(100) NOT NULL,
active_flag CHAR(1) DEFAULT 'N' NOT NULL
)
CREATE TABLE rruser_group (
user_id NUMBER(32,0) NOT NULL,
group_id NUMBER(32,0) NOT NULL
)
CREATE TABLE rrgroup (
id NUMBER(32,0) NOT NULL,
name VARCHAR2(100) NOT NULL,
code VARCHAR2(20) NOT NULL
)
CREATE TABLE rrgroup_permission (
group_id NUMBER(32,0) NOT NULL,
permission_id NUMBER(32,0) NOT NULL
)
CREATE TABLE rrpermission (
id NUMBER(32,0) NOT NULL,
name VARCHAR2(100) NOT NULL,
description VARCHAR2(1000) NOT NULL
)
The connectivity is such that RRUSER is linked to RRGROUP via table RRUSER_GROUP
and RRGROUP further linked with RRPERMISSION via table RRGROUP_PERMISSION.
I have to find out the users whose active flag value is equal to 'Y' within RRUSER; I'm using the below query
SELECT * FROM rruser WHERE ACTIVE_FLAG = 'Y'
I then have to find out the users which have write permission; in the last table, RRPERMISSION, there is a column NAME, which has the write permissions where the value of this column is 'write'. What query could I use to get this information? I know it should be achieved using an INNER JOIN.
Right now, I have tried a query for particular user to know whether he has write permission or not and have found out that he has write permission like this...
SELECT count(ID) FROM rruser WHERE ACTIVE_FLAG = 'Y';
SELECT * FROM rruser WHERE ACTIVE_FLAG = 'Y' AND FULLNAME = 'sss'
SELECT * FROM rruser_group WHERE USER_ID = 1100
SELECT * FROM rrgroup WHERE ID = 113;
SELECT * FROM rrgroup_permission WHERE GROUP_ID = 189 ;
SELECT * FROM rrpermission WHERE ID = 990
Try this:
SELECT ru.* FROM rruser ru
inner join rruser_group rg ON ru.id = rg.user_id
inner join rrgroup_permission rgp ON rg.group_id = rgp.group_id
inner join rrpermission rp ON rgp.permission_id = rp.id WHERE ru.ACTIVE_FLAG='Y' AND rp.name='write'

Update: subquery returned more than 1 value

I am having a problem, and I can't figure out how to fix this query. I have a temp table, one of the columns should contain a calculated value of another column divided by a sum of groups of that column. I don't know how to write this so that I avoid the error.
Declare #Temp Table
(
ZipCode char(5) Not Null,
StateFacilityId varchar (50) Not Null,
Cnt int Not Null,
MarketShare float,
Row int Not Null,
Primary Key Clustered (ZipCode, StateFacilityId)
);
Insert Into #Temp (ZipCode, StateFacilityId, Cnt, Row)
Select d.ZipCode, d.StateFacilityId, Cnt = COUNT(*), Row = ROW_NUMBER()OVER(PARTITION BY ZipCode ORDER BY Count(*) DESC)
From [MarketShareIQData].[dbo].[tblServicesDetail] d
Group By d.ZipCode, d.StateFacilityId
;
Update #Temp
Set MarketShare =(h.Cnt/(
Select SUM(h.Cnt)
From #Temp h
Group By ZipCode
))
From #Temp h
A group by would return one row per group. I'm guessing you're looking for the single group with matching zipcode. You could do that like:
update h
set MarketShare = h.Cnt /
(
select sum(h2.Cnt)
from #Temp h2
where h2.ZipCode = h.ZipCode
)
from #Temp h