Merge Three Tables and get one output using SQL query - c#

I have below candidate table details
Table_TraineeInfo
TraineeID BatchId Name Mobile
--------------------------------------------------
243 45 demo201 9888562341
244 45 demo202 9888562342
246 45 demo204 9888562344
This is my batch details of above candidate have reference id 45 in both common tables
Table_Batch_Lookup
BatchId BatchStartDate BatchEndDate
------------------------------------------------------------------------
45 2019-11-27 00:00:00.000 2019-11-29 23:59:59.000
Below is my Trainee attendance log table have common between Table_TraineeInfo and Table_Attendance_Log is TraineeID
Table_Attendance_Log
TraineeID BatchId Attendance Date
------------------------------------------------------------
243 45 Present 2019-11-27 17:55:56.513
243 45 Present 2019-11-28 17:58:06.220
243 45 Absent 2019-11-29 18:00:56.820
244 45 Present 2019-11-29 18:05:03.930
246 45 Absent 2019-11-28 18:09:08.567
246 45 Present 2019-11-29 18:09:08.567
I want output like below merge the three tables and get one output as batch candidate attendance report using a SQL query or possible way.
TraineeID BatchId Name Mobile 2019-11-27 2019-11-28 2019-11-29 Score
-----------------------------------------------------------------------------------------------------------------------------
243 45 demo201 9888562341 Present Present Absent 3/2
244 45 demo202 9888562342 No Record No Record Present 3/1
246 45 demo204 9888562344 No Record Absent Present 3/1
I will explain above output first four columns will fill using Table_TraineeInfo and next dataes will fill base on BatchStartDate and BatchEndDate from Table_Batch_Lookup and
Present and absent will base on Table_Attendance_Log no data availabe in attendacne list then fill no record, finally score Present will 1 value and out of 3 days.

I'm not sure how close to solution it but you may need dynamic pivot.
please try below:
CREATE TABLE Table_TraineeInfo (TraineeID int,BatchId int,Name varchar(max),Mobile varchar(10))
INSERT INTO Table_TraineeInfo VALUES(243, 45 , 'demo201' , '9888562341')
INSERT INTO Table_TraineeInfo VALUES(244, 45 , 'demo202' , '9888562342')
INSERT INTO Table_TraineeInfo VALUES(246, 45 , 'demo204' , '9888562344')
CREATE TABLE Table_Attendance_Log (TraineeID INT, BatchId INT, Attendance VARCHAR(10) , l_date DATETIME)
INSERT INTO Table_Attendance_Log VALUES (243, 45 , 'Present' ,'2019-11-27 17:55:56.513')
INSERT INTO Table_Attendance_Log VALUES (243, 45 , 'Present' ,'2019-11-28 17:58:06.220')
INSERT INTO Table_Attendance_Log VALUES (243, 45 , 'Absent' ,'2019-11-29 18:00:56.820')
INSERT INTO Table_Attendance_Log VALUES (244, 45 , 'Present' ,'2019-11-29 18:05:03.930')
INSERT INTO Table_Attendance_Log VALUES (246, 45 , 'Absent' ,'2019-11-28 18:09:08.567')
INSERT INTO Table_Attendance_Log VALUES (246, 45 , 'Present' ,'2019-11-29 18:09:08.567')
CREATE TABLE Table_Batch_Lookup (BatchId int , BatchStartDate DATETIME , BatchEndDate DATETIME)
INSERT INTO Table_Batch_Lookup VALUES( 45 , '2019-11-27 00:00:00.000', '2019-11-29 23:59:59.000')
Date CTE
Declare #cols NVARCHAR(Max)='';
;With log_date AS (
SELECT BatchStartDate as l_date FROM Table_Batch_Lookup
UNION ALL
SELECT DATEADD(dd, 1, l_date) FROM log_date AS ld , Table_Batch_Lookup AS tb WHERE ld.l_date<DATEADD(dd, -1, tb.BatchEndDate)
)
SELECT #cols = COALESCE (#cols + ',[' + CONVERT(NVARCHAR,CONVERT(VARCHAR(10), l_Date, 111), 106) + ']','[' + CONVERT(NVARCHAR, l_Date, 106) + ']') FROM (SELECT DISTINCT CONVERT(VARCHAR(10), l_Date, 111) AS l_date FROM log_date) PV;
Dynamic Pivot
Declare #totalScore INT =len(#cols) - len(replace(#cols, ',', ''))
CREATE TABLE #scoreTable (TraineeID int,Score Varchar(max))
INSERT INTO #scoreTable SELECT TraineeID,(CAST (#totalScore AS VARCHAR(10)) +'/'+CAST (SUM(CASE WHEN Attendance='Present' THEN 1 ELSE 0 END) AS VARCHAR(10)))AS Score from Table_Attendance_Log GROUP BY TraineeID;
--SELECT * from #scoreTable
DECLARE #query NVARCHAR(MAX);
SET #query = 'SELECT t_info.TraineeID,t_batch.BatchId,t_info.Name,t_info.Mobile'+#cols+' ,s.Score FROM Table_TraineeInfo AS t_info JOIN
(SELECT * FROM
(
SELECT TraineeID,BatchId,Attendance,CONVERT(VARCHAR(10), l_Date, 111) AS l_date FROM Table_Attendance_Log
) x
PIVOT
(
MAX(Attendance)
FOR l_Date IN (' + right(#cols, len(#cols)-1)+ ')
) p ) AS f_pv ON t_info.TraineeID=f_pv.TraineeID
JOIN Table_Batch_Lookup as t_batch ON t_batch.BatchId=t_info.BatchId
JOIN #scoreTable AS s ON t_info.TraineeID=s.TraineeID
WHERE t_batch.BatchId=45;
' ;
EXEC SP_EXECUTESQL #query;
output:
TraineeID BatchId Name Mobile 2019/11/27 2019/11/28 2019/11/29 Score
243 45 demo201 9888562341 Present Present Absent 3/2
244 45 demo202 9888562342 Present 3/1
246 45 demo204 9888562344 Absent Present 3/1
Demo

Impossible to create one query with different column count and column
names.
The workaround is creating a script for dynamic SQL query forming. Or I can write a query with columns named [day1],[day2],...,[dayN]...
if between BatchEndDate and BatchStartDate always the same day's count.

Related

how to get last_date balance record when we select DATA by date_range

for example:
If I apply date_range of '16-May-2019' for know the balance_Column_Record,
then DataGridview should show '15-May-2019's Last balance record even though 15-May-2019 may have many records but just show the last record.
so the first line of the Grid show >> Opening Balance '5000'
then the applied data show the desired output
I got the following output, but i want to show the output with previous one balance_amount of just before the applied date_range selected
Date Desc Dr Cr Balance
16-May-19 Service loans 5000 0 5000
17-May-19 Service expanse 3000 0 8000
18-May-19 Loan Deduction 2000 0 10000
19-May-19 Bike Loan 0 5000 5000
20-May-19 Office Expanse 0 2000 3000
21-May-19 Lunch Expanse 8000 0 11000
I expecting the output like this,
Date Desc Dr Cr Balance
15-May-19 Balance >>> - - 8000
16-May-19 Service loans 5000 0 13000
17-May-19 Service expanse 3000 0 16000
18-May-19 Loan Deduction 2000 0 18000
19-May-19 Bike Loan 0 5000 13000
20-May-19 Office Expanse 0 2000 11000
21-May-19 Lunch Expanse 8000 0 19000
This may help...
DECLARE #Temp TABLE
(
Id INT IDENTITY(1,1),
[Date] DATE,
[Desc] VARCHAR(100),
DR DECIMAL(18,2),
CR DECIMAL(18,2),
Balance DECIMAL(18,2)
)
INSERT INTO #Temp(Date,[Desc],DR,CR,Balance) VALUES ('15-May-19','Service loans',1000,0,1000)
INSERT INTO #Temp(Date,[Desc],DR,CR,Balance) VALUES ('15-May-19','Service loans',2000,0,3000)
INSERT INTO #Temp(Date,[Desc],DR,CR,Balance) VALUES ('16-May-19','Service loans',5000,0,8000)
INSERT INTO #Temp(Date,[Desc],DR,CR,Balance) VALUES ('17-May-19','Service loans',3000,0,11000)
INSERT INTO #Temp(Date,[Desc],DR,CR,Balance) VALUES ('18-May-19','Service loans',0,1000,10000)
DECLARE #FromDate DATE = '16-May-19'
DECLARE #ToDate DATE = '18-May-19'
;WITH Lastbalance
AS(
SELECT TOP(1) Date,'Balance >>>' [Desc],'--'DR,'--'CR,Balance
FROM #Temp
WHERE Date = dateadd(day,-1, cast(#FromDate as date))
ORDER BY Id DESC
)
SELECT * FROM Lastbalance
UNION
SELECT Date,[Desc],CAST(DR as VARCHAR(10)) DR,CAST(CR as VARCHAR(10)) CR,Balance
FROM #Temp
WHERE Date >= #FromDate AND Date <= #ToDate
I think you want something like this:
select t.*
from (select t.*,
sum(dr - cr) over (order by date) as balance
from t
) t
where date >= '2019-05-15';
In other words, calculate the balance in a subquery. Then do the filtering in the outer query.
What type of grid are you using? A DataGridview? You could write one query for the previous balance and insert one grid row for that result then the remaining grid rows with the applied data.
DataGridView.Rows.Add(row);

Query for match all records in list SQL Server

I have a table bawe_services. i want to fetch all data that match with given keys
like i have fields
id | Service_id |bawe_id
1 2 2
2 3 3
3 2 3
if i pass service =2 i need all record of service_id=2 if i pass service=1,2,3 than i want 0 rows because 1 service is not given by any bawe so. i got 0 rows.
I use this query
select * from aspnet_bawe_services where ser_id in(1,2,3)
Thanx in advance
The count of the parameters in the "in" statement must match the having equal number.
select bawe_id from [dbo].[aspnet_bawe_services]
where Service_id in (2)
group by bawe_id
having count(Service_id)=1;
bawe_id
-----------
2
3
select bawe_id from [dbo].[aspnet_bawe_services]
where Service_id in (2,3)
group by bawe_id
having count(Service_id)=2;
bawe_id
-----------
3
select bawe_id from [dbo].[aspnet_bawe_services]
where Service_id in (1,2,3)
group by bawe_id
having count(Service_id)=3;
bawe_id
-----------
(0 row(s) affected)
TRY THIS: It's really tedious but unique requirement and I think to accomplish this, we have to use function
1-Function returns distinct count of service_id
2-Function to split comma separated value and return in table format
--Function returns distinct count of service_id
CREATE FUNCTION [dbo].[getCount](#service_id varchar(500))
RETURNS INT
AS
BEGIN
DECLARE #count int
SELECT #count = COUNT(DISTINCT(t.service_id))
FROM tmptos t
INNER JOIN [dbo].[SplitValue](#service_id, ',') tt on t.service_id = tt.items
RETURN #count
END;
--Function to split comma separated value and return in table format
--Function copied from
--separate comma separated values and store in table in sql server
CREATE FUNCTION [dbo].[SplitValue](#String varchar(MAX), #Delimiter char(1))
RETURNS #temptable TABLE (items VARCHAR(MAX))
AS
BEGIN
DECLARE #idx int
DECLARE #slice varchar(8000)
SELECT #idx = 1
if len(#String)<1 or #String is null return
WHILE #idx!= 0
BEGIN
set #idx = charindex(#Delimiter,#String)
IF #idx!=0
set #slice = left(#String,#idx - 1)
else
set #slice = #String
IF(LEN(#slice)>0)
INSERT INTO #temptable(Items) values(#slice)
SET #String = right(#String,len(#String) - #idx)
IF LEN(#String) = 0 break
END
RETURN
END;
--Table with Sample Data
create table tmptos(id int, Service_id int, bawe_id int)
insert into tmptos values
(1, 2, 2),
(2, 3, 3),
(3, 2, 3)
declare #service_id varchar(50) = '2,3'
select *
from tmptos t
inner join [dbo].[SplitValue](#service_id, ',') tt on t.Service_id = tt.items
where [dbo].[getCount](#service_id) = (select count(distinct(items)) from [dbo].[SplitValue](#service_id, ','))
OUTPUT:
id Service_id bawe_id items
1 2 2 2
2 3 3 3
3 2 3 2
It's bit lengthy but works perfectly.
select * from aspnet_bawe_services
where Service_id in (1,2,3)
and
( select count(distinct Service_id) from aspnet_bawe_services where Service_id in (1,2,3) ) = 3
last number in query (in this case "3") is elements count, which you have in IN list.
You can get the service ids that you want using group by and having:
select service_id
from t
where bawe_id in (1, 2, 3)
group by service_id
having count(distinct bawe_id) = 3;
The "= 3" is the number of ids in the IN list.
You can then use in or join or exists to get the full records:
select t.*
from t
where t.service_id in (select service_id
from t
where bawe_id in (1, 2, 3)
group by service_id
having count(distinct bawe_id) = 3
);

Get data from SQLServer in ASC order

I have a table with column name id and value. While data is being saved in sql server database, it sorts itself in random order, i.e id value 1,2,3,4,5,6,7,14,15,16,17,8,9,10 and likewise.
I need to retrieve data in 4 groups with each having 11 data in asc id order,
that is,
Group 1: 1-11
Group 2 : 12-22
Group 3 : 23-33
Group 4 : 33-44
I have tried query
Group 1:select top(11) * from tblCode order by id ASC
Group 2:SELECT top(22)* FROM tblCode except select top(11) * from tblCode order by id ASC
Group 3:SELECT top(33)* FROM tblCode except select top(22) * from tblQRCode order by id ASC
group 4:SELECT top(44)* FROM tblCode except select top(33) * from tblCode order by id ASC
What my problem is since data are sorted randomly while saving them into database, they are retrieved randomly.
Below is the screenshot of how my data are saved in database.
help me select data as above mentioned group.
Use OFFSET and FETCH rather than TOP.
E.g. Group two would be:
select *
from tblCode
order by id ASC
offset 11 rows
fetch next 11 rows only
Complete repro script:
declare #t table (ID int not null, Value varchar(93) not null);
;With Numbers as (
select ROW_NUMBER() OVER (ORDER BY so1.object_id) as n
from sys.objects so1,sys.objects so2,sys.objects so3
)
insert into #t (ID,Value)
select n,'PEC-' + CONVERT(varchar(93),n)
from Numbers
where n between 1 and 1000
select *
from #t
order by id ASC
offset 11 rows
fetch next 11 rows only
Result:
ID Value
----------- ---------
12 PEC-12
13 PEC-13
14 PEC-14
15 PEC-15
16 PEC-16
17 PEC-17
18 PEC-18
19 PEC-19
20 PEC-20
21 PEC-21
22 PEC-22
This also get your desired results. For other queries change 33 with other values, now it get values from 33 to 22.
WITH t AS
( SELECT ROW_NUMBER() OVER (ORDER BY id) AS row_num, *
FROM tblCode )
SELECT TOP 11 *
FROM t
WHERE row_num > 33
Try this,
select * from Table Name Order by ID
I hope I am not misunderstand:
--Group 1
SELECT *
FROM tblCode
WHERE id >= 1
AND id <= 11
ORDER BY id ASC
--Group 2
SELECT *
FROM tblCode
WHERE id >= 12
AND id <= 22
ORDER BY id ASC
--Group 3
SELECT *
FROM tblCode
WHERE id >= 23
AND id <= 33
ORDER BY id ASC
You can also save the increments in variable. Maybe something like this (i.e) you send param group no 3:
--Group 3
SELECT #Group = 3 --just for sample, param should sent From application
SELECT #lastIndex = 3*11
SELECT #indexStart = #lastIndex - 10
SELECT *
FROM tblCode
WHERE id >= #indexStart
AND id <= #lastIndex
ORDER BY id ASC

How to insert Duplicated data only into an Event Log?

I need help regarding a SQL query problem. I have a query where I am able to delete the duplicates but I also need to create records of the duplicated data being deleted into a EventLog in which I am clueless about it. Below is an example of my Student Table. From the table below, you can see only Alpha and Bravo are duplicated
id Name Age Group
-----------------------
1 Alpha 11 A
2 Bravo 12 A
3 Alpha 11 B
4 Bravo 12 B
5 Delta 11 B
As I am copying data from Group A to Group B, I need to find & delete the duplicated data in group B. Below is my query on deleting duplicates from Group B.
DELETE Student WHERE id
IN (SELECT tb.id
FROM Student AS ta
JOIN Student AS tb ON ta.name=tb.name AND ta.age=tb.age
WHERE ta.GroupName='A' AND tb.GroupName='B')
Here is an example of my eventlog and how I want the query that I execute to like.
id Name Age Group Status
------------------------------------------
1 Alpha 11 B Delete
2 Bravo 11 B Delete
Instead of inserting the entire Group B data into the eventlog, is there any query that can just insert the Duplicated Data into the event log?
If we are speaking about Microsoft sql, key is output clause, more details here https://msdn.microsoft.com/en-us/library/ms177564.aspx
declare #Student table
( id int, name nvarchar(20), age int,"groupname" char(1))
insert into #student values (1, 'Alpha' , 11, 'A' ),
(2, 'Bravo' , 12, 'A'),
(3 ,'Alpha' , 11 , 'B'),
(4 ,'Bravo' ,12 , 'B'),
(5 ,'Delta' ,11 , 'B')
declare #Event table
( id int, name nvarchar(20), age int,"groupname" char(1),"Status" nvarchar(20))
select * from #Student
DELETE #Student
output deleted.*, 'Deleted' into #Event
WHERE id
IN (SELECT tb.id
FROM #Student AS ta
JOIN #Student AS tb ON ta.name=tb.name AND ta.age=tb.age
WHERE ta.GroupName='A' AND tb.GroupName='B')
select * from #event
Run this before the Delete above. Not sure how you decide what one is the duplicate but you can use Row_Number to list them with the non duplicate at as 1 and and then insert everything with a row_Number > 1
; WITH cte AS
(
SELECT Name
,Age
,[Group]
,STATUS = 'Delete'
,RID = ROW_NUMBER ( ) OVER ( PARTITION BY Name,Age ORDER BY Name)
FROM Student AS ta
JOIN Student AS tb ON ta.name=tb.name AND ta.age=tb.age
)
INSERT INTO EventLog
SELECT Name,Age,[Group],'Delete'
FROM cte
WHERE RID > 1
you need to create basic trigger after delete in student table, this query will be executed after any deletion process in student table and will insert deleted record into log_table
create trigger deleted_records
on student_table
after delete
as
begin
insert into log_table
select d.id, d.Name, d.Age, d.Group, 'DELETED'
from DELETED d;
end

Trigger to edit a table column depending on another column from another table

I have these table's:
CREATE TABLE Functions
(
id_f NUMBER (5) NOT NULL,
id_gFK NUMBER (5) NOT NULL
) ;
--id_gFK is foreign key from Salaries table
CREATE TABLE Salaries
(
id_g NUMBER (5) NOT NULL ,
g1_g1 NUMBER (6) ,
g1_g2 NUMBER (6) ,
g1_g3 NUMBER (6) ,
g2_g1 NUMBER (6) ,
g2_g2 NUMBER (6) ,
g2_g3 NUMBER (6) ,
g3_g1 NUMBER (6) ,
g3_g2 NUMBER (6) ,
g3_g3 NUMBER (6)
) ;
--g1_g1 means - grade 1, gradation 1
CREATE TABLE Employee
(
id_e NUMBER (5) NOT NULL ,
id_fFK NUMBER (5) NOT NULL ,
grade NUMBER (1) ,
gradation NUMBER (1) ,
salary NUMBER (6)
) ;
--id_fFK is foreign key from Functions table
When I am inserting an employee, everything is working, and he is taking the salary depending on that column. But, I am able to edit a salary in Salaries table.
For example:
id_g=101, g1_g1=5000....g3_g3=1200 in Salaries table
id_f=201, id_gFK=101 in Functions table
id_e=1001, id_fFK=201, grade=1, gradation=1, salary=5000 in Employee table (5000 was taken from Salaries table depending on id_fFK, grade and gradation).
After I will edit that salary, 5000, in Salaries table, for example, 4000, I want this edit to be done automaticaly in Employee table.
I am sure that I can do this with a trigger. I have tried, but nothing.
Can anyone tell me what kind of trigger I have to use and how?
Thank you!!!
Please see the below example where am updating and inserting a record to emp_sal table and the same is getting inserted to employee table via trigger.
CREATE OR REPLACE TRIGGER change_salary
AFTER INSERT OR UPDATE ON emp_sal
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO Employee (employee_id,first_name,salary)
values (:new.eno,:new.ename,:new.sal);
END IF;
IF UPDATING THEN
UPDATE Employee
SET salary = nvl(:NEW.sal,:old.sal)
WHERE employee_id = :new.eno;
end if;
END;
Execution:
SQL> select sal from emp_sal where eno = 3 ;
SAL
----------
80006
SQL> select salary from employee where employee_id = 3;
SALARY
----------
50000
SQL> update emp_sal
set sal = 1234
where eno = 3 ;
1 row updated.
SQL> commit;
Commit complete.
SQL> select sal from emp_sal where eno = 3 ;
SAL
----------
1234
SQL> select salary from employee where employee_id = 3;
SALARY
----------
1234
SQL> insert into emp_sal(eno,ename,sal)
values
(9,'XING',40000);
1 row created.
SQL> commit;
Commit complete.
SQL> select salary from employee where employee_id =9;
SALARY
----------
40000
EDIT: Am not sure what is your trigger but i do see that there is wrong data set up. I read your requirement and did my way and it working and tested. See below:
Tables with data:
CREATE TABLE Salaries
(
id_g NUMBER (5) NOT NULL PRIMARY KEY ,
g1_g1 NUMBER (6) ,
g1_g2 NUMBER (6) ,
g1_g3 NUMBER (6) ,
g2_g1 NUMBER (6) ,
g2_g2 NUMBER (6) ,
g2_g3 NUMBER (6) ,
g3_g1 NUMBER (6) ,
g3_g2 NUMBER (6) ,
g3_g3 NUMBER (6)
) ;
Insert into SALARIES
Values
(101, 5000, 2000, 3000, 4000,
6000, 7000, 8000, 6000, 12000);
COMMIT;
--------------------------------
CREATE TABLE Functions
(
id_f NUMBER (5) NOT NULL,
id_gFK NUMBER (5) NOT NULL PRIMARY KEY,
CONSTRAINT fk_sal
FOREIGN KEY (id_gFK)
REFERENCES Salaries(id_g)
) ;
Insert into FUNCTIONS
(ID_F, ID_GFK)
Values
(201, 101);
COMMIT;
--------------------------
CREATE TABLE Employees
(
id_e NUMBER (5) NOT NULL ,
id_fFK NUMBER (5) NOT NULL ,
grade NUMBER (1) ,
gradation NUMBER (1) ,
salary NUMBER (6),
CONSTRAINT fk_id_emp
FOREIGN KEY (id_fFK)
REFERENCES Functions(id_gFK)
) ;
Insert into EMPLOYEES
(ID_E, ID_FFK, GRADE, GRADATION, SALARY)
Values
(101, 101, 1, 1, 5000);
COMMIT;
Trigger:
CREATE OR REPLACE TRIGGER change_salary_new
AFTER UPDATE ON Salaries
FOR EACH ROW
BEGIN
UPDATE Employees
SET salary = nvl(:NEW.g1_g1,:old.g1_g1)
WHERE id_e = :new.id_g;
End ;
Execution :
SQL> select id_g,g1_g1 from Salaries;
ID_G G1_G1
---------- ----------
101 5000
SQL> select id_e,salary from Employees;
ID_E SALARY
---------- ----------
101 5000
SQL> update Salaries set g1_g1 = 10202 where id_g = 101;
1 row updated.
SQL> commit;
Commit complete.
SQL> select id_e,salary from Employees;
ID_E SALARY
---------- ----------
101 10202

Categories

Resources