Related
I have two tables in my database:
/* Create class table */
CREATE TABLE Class
(
CId INT NOT NULL,
ClassName VARCHAR (50) NOT NULL,
ClassDescription VARCHAR (MAX) NULL,
ClassStatus VARCHAR (50) NOT NULL,
StartDate DATE NOT NULL,
EndDate DATE NOT NULL,
Degree VARCHAR (50) NOT NULL,
TeacherName VARCHAR (50) NOT NULL,
ClassTopic VARCHAR (50) NOT NULL,
CONSTRAINT CHK_Dates CHECK (EndDate > StartDate),
CONSTRAINT CHK_Status CHECK (ClassStatus = 'Active' Or ClassStatus = 'NAct' Or ClassStatus = 'Archive'),
CONSTRAINT CHK_Degree CHECK (Degree = '1st' Or
Degree = '2nd' Or
Degree = '3rd' Or
Degree = '4th' Or
Degree = '5th' Or
Degree = '6th' Or
Degree = '7th' Or
Degree = '8th' Or
Degree = '9th' Or
Degree = '10th' Or
Degree = '11th' Or
Degree = '12th'),
CONSTRAINT CHK_Topic CHECK (ClassTopic = 'Mth' Or
ClassTopic = 'Ph' Or
ClassTopic = 'Ch' Or
ClassTopic = 'Bio' Or
ClassTopic = 'Fr' Or
ClassTopic = 'En' Or
ClassTopic = 'Arb' Or
ClassTopic = 'Rgs'),
PRIMARY KEY (CId)
);
/* Create Student table*/
CREATE TABLE Student
(
_SId INT NOT NULL,
UserName VARCHAR (50) NOT NULL,
Bdate DATE NOT NULL,
SPassword VARCHAR (50) NOT NULL,
SName VARCHAR (50) NOT NULL,
SLastName VARCHAR (50) NOT NULL,
NationalCode VARCHAR (10) NOT NULL,
Email VARCHAR (MAX) NULL,
StudentClass INT NOT NULL,
HomePhone VARCHAR (8) NOT NULL,
CONSTRAINT CHK_Email CHECK (Email like '%_#__%.__%'),
PRIMARY KEY (_SId),
FOREIGN KEY (StudentClass) REFERENCES Class (CId) ON DELETE CASCADE
);
ALTER TABLE Student
ADD CONSTRAINT New_CHK_NCode CHECK (NationalCode LIKE '%[0-9]%');
ALTER TABLE Student
ADD CONSTRAINT New_CHK_Phone CHECK (HomePhone LIKE '%[0-9]%');
I have inserted the below records in each of them:
USE School
/* Inserting data into tables */
INSERT INTO dbo.Class (CId, ClassName, ClassDescription, ClassStatus, StartDate, EndDate, Degree, TeacherName, ClassTopic)
VALUES (1, 'aaa', NULL, 'Active', '20020907', '20030907', '1st','Eetemadi', 'Mth'),
(2, 'bbb', NULL, 'Active', '20020907', '20030907', '1st','Rahmani', 'Ph'),
(3, 'ccc', NULL, 'Active', '20020907', '20030907', '2nd','Entezari', 'Ch'),
(4, 'ddd', NULL, 'Active', '20020907', '20030907', '2nd','Beytollahi', 'Bio'),
(5, 'eee', NULL, 'Active', '20020907', '20030907', '3rd','Zahirpour', 'Fr');
INSERT INTO dbo.Student (_SId, UserName, Bdate, SPassword, SName, SLastName, NationalCode, Email, StudentClass, HomePhone)
VALUES (1, 'aaa', '20020807', '1234', 'maryam', 'vahdati', '1234567890', 'mar#gmail.com', 1, '12345678'),
(2, 'bbb', '20020707', '4321', 'marjan', 'vahdati', '1234578906', 'marj#gmail.com', 1, '12345678'),
(3, 'ccc', '20020607', '1342', 'masomeh', 'vahdati', '1234567809', 'mas#gmail.com', 2, '12345678'),
(4, 'ddd', '20020507', '1243', 'mohammad', 'vahdati', '1234568907', 'moh#gmail.com', 2, '12345678'),
(5, 'eee', '20020407', '1342', 'mahmod', 'vahdati', '1245678903', 'mah#gmail.com', 3, '12345678');
Now I have to write a query in a Procedure to group classes based on degrees, show the number of students in every grade, and also the total number of classes.
I have written the below query:
ALTER PROCEDURE dbo.FirstReport
AS
BEGIN
Select Degree, COUNT(Degree) as numberOfClasses, COUNT(StudentClass) as numberOfStudents
FROM Class C left outer join Student S ON C.CId = S.StudentClass
GROUP BY Degree
UNION ALL
SELECT 'SUM' Degree, COUNT(Degree), COUNT(StudentClass)
FROM Class C join Student S ON C.CId = S.StudentClass;
END
The output is:
| Degree| numberOfClasses | numberOfStudents|
|:-----------------------------------------:|
|1st | 4 | 4 |
|2nd | 2 | 1 |
|3rd | 1 | 0 |
|SUM | 5 | 5 |
But the numberOfClasses must be 2 when the Degree is 1st.
I do not know how to make it correct. I will be grateful for your help.
As I mentioned in the comments, you have a many to one join here, and thus the COUNT you get is correct, due said one to many join; you have 2 rows that each join to 2 other rows and 2 * (1 * 2) = 4.
Instead, use a DISTINCT on your first COUNT on the ID column. Also, there's no need for a UNION ALL; you can use GROUPING SETS or ROLLUP to get the "grand total" row:
SELECT ISNULL(C.Degree,'SUM') AS Degree,
COUNT(DISTINCT C.CId) AS NumberOfClasses,
COUNT(S.StudentClass) AS NumberOfStudents
FROM dbo.Class C
LEFT OUTER JOIN dbo.Student S ON C.CId = S.StudentClass
GROUP BY GROUPING SETS(Degree,());
I wish to delete all the rows with meaning 'Yellow' in col1 if during 24 hours follows something not null in the col2 or something not null and not 'Yellow' in the col1 (inside the same client)
The dataset is:
client datetime col1 col2
-----------------------------------------------
1 2019-05-20 14:30:08.000 NULL Green
1 2019-05-31 12:23:55.000 Yellow NULL
1 2019-05-31 13:00:08.000 NULL Green
2 2019-09-10 12:22:17.000 Yellow NULL
2 2019-09-10 13:30:57.000 NULL Green
3 2019-09-14 07:34:41.000 Red NULL
3 2019-09-13 07:45:24.000 Yellow NULL
4 2019-01-15 14:15:08.000 Yellow NULL
4 2019-01-15 14:48:40.000 NULL Green
5 2019-06-24 16:13:29.000 NULL Green
5 2019-06-25 13:14:07.000 Yellow NULL
6 2019-09-06 11:22:19.000 Yellow NULL
6 2019-09-06 12:19:48.000 NULL Green
6 2019-10-08 12:19:08.000 Red NULL
If there is some value in the col1, col2 = null and backwards.
Rows that should be deleted:
client datetime col1 col2
---------------------------------------------
1 2019-05-31 12:23:55.000 Yellow NULL
2 2019-09-10 12:22:17.000 Yellow NULL
3 2019-09-13 07:45:24.000 Yellow NULL
4 2019-01-15 14:15:08.000 Yellow NULL
6 2019-09-06 11:22:19.000 Yellow NULL
This can be done using a simple exists:
First, create and populate sample table (Please save us this step in your future questions)
DECLARE #T As Table (
[client] int,
[datetime] datetime,
[col1] varchar(6),
[col2] varchar(5)
);
INSERT INTO #T ([client], [datetime], [col1], [col2]) VALUES
(1, '2019-05-20T14:30:08', NULL, 'Green'),
(1, '2019-05-31T12:23:55', 'Yellow', NULL),
(1, '2019-05-31T13:00:08', NULL, 'Green'),
(2, '2019-09-10T12:22:17', 'Yellow', NULL),
(2, '2019-09-10T13:30:57', NULL, 'Green'),
(3, '2019-09-14T07:34:41', 'Red', NULL),
(3, '2019-09-13T07:45:24', 'Yellow', NULL),
(4, '2019-01-15T14:15:08', 'Yellow', NULL),
(4, '2019-01-15T14:48:40', NULL, 'Green'),
(5, '2019-06-24T16:13:29', NULL, 'Green'),
(5, '2019-06-25T13:14:07', 'Yellow', NULL),
(6, '2019-09-06T11:22:19', 'Yellow', NULL),
(6, '2019-09-06T12:19:48', NULL, 'Green'),
(6, '2019-10-08T12:19:08', 'Red', NULL);
Then, the delete statement:
DELETE t1
FROM #T AS t1
WHERE t1.col1 = 'Yellow'
AND EXISTS
(
SELECT 1
FROM #T t2
WHERE t2.[datetime] > t1.[datetime]
AND t2.[datetime] <= DATEADD(HOUR, 24, t1.[datetime])
AND t2.[client] = t1.[client]
AND (
t2.[col2] IS NOT NULL
OR t2.[col1] <> 'Yellow'
)
)
Live demo
I want create 2 data rows with 1 same findable unique id to each one by 1 query
2 difference is side column {1 buyer} {0 seller } and userId column {userID's}
id userId side price qty pairId
1 6 0 60 10 1
2 9 1 60 10 1
trying to visualize result table:
In SQL Server I tried SCOPE_IDENTITY()
insert into [dbo].[deals] (side, price, qty,pairId)
values (1, 60, 10 ,SCOPE_IDENTITY()),
(0, 60, 10 ,SCOPE_IDENTITY()),
create table command:
CREATE TABLE [demonstration].[dbo].[Deals](
[id] [bigint] IDENTITY(1,1) NOT NULL,
[userId] [int] NULL,
[side] [smallint] NULL,
[qty] [decimal](18, 4) NULL,
[price] [decimal](18, 4) NULL,
[pairId] [bigint] NULL
) ON [PRIMARY]
GO
Add an IDENTITY column to deals table(or alter one column to identity) then use your query:
insert into [dbo].[deals] (side, price, qty,pairId)
values (1, 60, 10 ,IDENT_CURRENT('deals')+1),
(0, 60, 10 ,IDENT_CURRENT('deals')+1)
added +1
I have two tables, one with Events, the other with episodes.
An Episode has a start date and end date, the event has a single date.
Both Episodes and Events have one of six Types.
Currently I'm using some fuzzy logic to run an update script on the Events table to set it's ID field to the matching Episode. It does this by checking for the Event date between the Episode start and end, both having the same Type, as well as some other links like same User etc.
Since the Events can sit outside of the Episode, or have a different Type, what I do is loop through a sequence of expanding date ranges (StartDate-1, -2 etc) and also cycle through each Type looking for a match.
I've been reading that while loops aren't very efficient, so was wondering if there was a way to rewrite this nested loop into a CTE function.
I'm using SQL Server 2012.
Event List is just a temp table that has all the possible Types with an order to loop through.
My loop currently is:
WHILE #CurrBefore <= #Before and #CurrentAfter <= #After
BEGIN
SET #Row = 0
WHILE #Row <= #MaxRow
BEGIN
UPDATE P
SET P.ID = E.ID
FROM Event P
OUTER APPLY (SELECT TOP 1 E.Id, E.Type
FROM Episode E
WHERE E.User = P.User AND
E.Type = CASE WHEN #Row=0 THEN P.Event ELSE (SELECT Event FROM #EventList WHERE RN = #Row) END AND
P.Date BETWEEN E.StartDate-#CurrentBefore AND E.EndDate+#CurrentAfter
ORDER BY P.Date) E
WHERE P.ID = 0
INCREMENT #ROW CODE
END
INCREMENT #BEFORE/AFTER CODE
END
Sample Data:
IF OBJECT_ID('tempdb..#EventList') IS NOT NULL
BEGIN
DROP TABLE #EventList
CREATE TABLE #EventList(Event Varchar(50), RN INT);
INSERT INTO #EventList SELECT 'A', 1
INSERT INTO #EventList SELECT 'B', 2
INSERT INTO #EventList SELECT 'C', 3
INSERT INTO #EventList SELECT 'D', 4
INSERT INTO #EventList SELECT 'E', 5
INSERT INTO #EventList SELECT 'F', 6
END
CREATE TABLE dbo.Episode ([ID] INT, [Start] DateTime, [End] DateTime, [Type] varchar(1), [User] INT)
INSERT INTO [dbo].Episode ([ID], [Start], [End], [Type],[User])
VALUES
(1, '2018-07-01 10:00', '2018-07-02 14:00', 'A',10),
(2, '2018-07-05 6:00', '2018-07-06 13:00', 'A',11),
(3, '2018-07-03 9:00', '2018-07-04 8:00', 'B',10),
(4, '2018-07-02 15:00', '2018-07-03 7:00', 'B',12),
(5, '2018-07-01 1:00', '2018-07-02 8:00', 'C',13),
(6, '2018-07-01 6:00', '2018-07-01 8:00', 'D',11)
CREATE TABLE dbo.Event ([ID] INT, [Date] DateTime, [Type] varchar(1), [User] INT)
INSERT INTO [dbo].Event ([ID], [Date], [Type],[User])
VALUES
(0, '2018-07-01 12:00', 'A',10),
(0, '2018-07-05 15:00', 'A',11),
(0, '2018-07-03 13:00', 'C',10),
(0, '2018-07-10 9:00', 'B',12),
(0, '2018-07-01 5:00', 'C',10),
(0, '2018-07-01 10:00', 'D',11)
Expected result, Event now looks like this:
1 2018-07-01 12:00:00.000 A 10
2 2018-07-05 15:00:00.000 A 11
3 2018-07-03 13:00:00.000 C 10
0 2018-07-10 09:00:00.000 B 12
1 2018-07-01 05:00:00.000 C 10
6 2018-07-01 10:00:00.000 D 11
I don't know, if I fully got the logic, but this might help to get you running:
USE master;
GO
CREATE DATABASE TestDB
GO
USE TestDB;
GO
CREATE TABLE dbo.Episode ([ID] INT, [Start] DateTime, [End] DateTime, [Type] varchar(1), [User] INT)
INSERT INTO [dbo].Episode ([ID], [Start], [End], [Type],[User])
VALUES
(1, '2018-07-01 10:00', '2018-07-02 14:00', 'A',10),
(2, '2018-07-05 6:00', '2018-07-06 13:00', 'A',11),
(3, '2018-07-03 9:00', '2018-07-04 8:00', 'B',10),
(4, '2018-07-02 15:00', '2018-07-03 7:00', 'B',12),
(5, '2018-07-01 1:00', '2018-07-02 8:00', 'C',13),
(6, '2018-07-01 6:00', '2018-07-01 8:00', 'D',11)
CREATE TABLE dbo.[Event] ([ID] INT, [Date] DateTime, [Type] varchar(1), [User] INT)
INSERT INTO [dbo].[Event] ([ID], [Date], [Type],[User])
VALUES
(0, '2018-07-01 12:00', 'A',10),
(0, '2018-07-05 15:00', 'A',11),
(0, '2018-07-03 13:00', 'C',10),
(0, '2018-07-10 9:00', 'B',12),
(0, '2018-07-01 5:00', 'C',10),
(0, '2018-07-01 10:00', 'D',11)
GO
CREATE TABLE #EventList(Event Varchar(50), RN INT);
INSERT INTO #EventList VALUES ('A', 1),('B', 2),('C', 3),('D', 4),('E', 5),('F', 6);
WITH mathingEpisodes AS
(
SELECT ev.ID AS evID
,ev.[Date] AS evDate
,ev.[Type] AS evType
,ev.[User] AS evUser
,e1.RN AS evRN
,ep.ID AS epID
,ep.[Type] AS epType
,e2.RN AS epRN
FROM [Event] ev
LEFT JOIN Episode ep ON ev.[User]=ep.[User] AND ev.[Date] >= ep.[Start] AND ev.[Date] < ep.[End]
LEFT JOIN #EventList e1 ON ev.[Type]=e1.[Event]
LEFT JOIN #EventList e2 ON ep.[Type]=e2.[Event]
)
SELECT COALESCE(epID,Closest.ID) AS FittingEpisodeID
,me.evDate
,evType
,evUser
FROM mathingEpisodes me
OUTER APPLY(SELECT TOP 1 *
FROM Episode ep
CROSS APPLY(SELECT ABS(DATEDIFF(SECOND,me.evDate,ep.[Start])) AS DiffToStart
,ABS(DATEDIFF(SECOND,me.evDate,ep.[End])) AS DiffToEnd) Diffs
CROSS APPLY(SELECT CASE WHEN DiffToStart<DiffToEnd THEN DiffToStart ELSE DiffToEnd END AS Smaller) Diffs2
WHERE ep.[User] = me.evUser
AND me.epID IS NULL
ORDER BY Diffs2.Smaller
) Closest
ORDER BY evDate;
GO
USE master;
GO
DROP DATABASE TestDB;
GO
DROP TABLE #EventList
GO
The result
1 2018-01-07 05:00:00.000 C 10
6 2018-01-07 10:00:00.000 D 11
1 2018-01-07 12:00:00.000 A 10
3 2018-03-07 13:00:00.000 C 10
2 2018-05-07 15:00:00.000 A 11
4 2018-10-07 09:00:00.000 B 12
Some explanation
In the first cte I try to find fitting episodes (same user and date within range).
The second cte will compute the closest Episode for the same user in all cases, where the first cte did not succeed.
The only difference for this sample is the event for userId=12. My logic will bind this to the closest episode of this user (ID=4), while your expected output shows a zero in this place.
Anyway, my solution is fully set-based, therefore faster than a loop, and should be rather close to your needs. Try to adapt it...
UPDATE Some more thoughts...
I did not get the ghist of your #EventList... I bound the results into the set (you can make it visible by using SELECT * instead of the explicit column list. But this is - assumably - not what you meant...
I have a question about SQL Server: how to get flag values based on pid?
If name values A and Name value B corresponding id values is 1 then flag 1 other wise 0
CREATE TABLE [dbo].[TABLECHECK]
(
[ID] [INT] NULL,
[NAME] [VARCHAR](50) NULL,
[PID] [INT] NULL
) ON [PRIMARY]
GO
INSERT INTO [dbo].[TABLECHECK] ([ID], [NAME], [PID])
VALUES (1, N'A', 1), (0, N'B', 1), (1, N'A', 2),
(1, N'B', 2), (0, N'A', 3), (0, N'B', 3)
Based on this data, I want to get an output like this:
PID | Flag
-----+-------
1 | 0
2 | 1
3 | 0
My query:
select
pid, count(id),
case
when name in ('a', 'b') and id = 1
then 1
else 0
end
from
[TABLECHECK]
group by
pid
This query is resulting in an error.
Please tell me how to achieve this task in SQL Server.
If A & B always exists per PID, you can do this
SELECT PID,
Flag = CASE WHEN MIN(ID) = 1 THEN 1 ELSE 0 END
FROM TABLECHECK
GROUP BY PID
Since you are using SQL Server 2012, you can use MIN(), IIF() and GROUP BY
SELECT PID,
IIF(MIN(ID) = 1, 1, 0) Flag
FROM TABLECHECK
GROUP BY PID;