I have the below stored procedure which works fine.
CREATE DEFINER=`root`#`localhost` PROCEDURE `TxCountByDatePerod`(IN `fromdate` DATE, IN `todate` DATE, IN `department` VARCHAR(20), IN `status` VARCHAR(10), IN `ipg` VARCHAR(20))
SELECT d.dept_name,
s.service_name,
COUNT(*) AS all_row_count
FROM department AS d
INNER JOIN service s ON s.dept_id=d.dept_id
INNER JOIN transaction_detail t ON s.dept_id=t.dept_id
AND s.service_id=t.service_id
WHERE t.tx_begin_date BETWEEN fromdate AND todate
AND (t.dept_id = department
OR department ='null')
AND (t.tx_status = status
OR status ='null')
AND (t.pg_name = ipg
OR ipg ='null')
GROUP BY t.dept_id,
t.service_id
Above SP is written in MySQL to get the number of transactions for a given time duration, provided by a particular service of a particular department. A transactions can be 3 different transaction statuses(tx_status coulmn) which are completed(1), failed(-1), uncompleted(0).
What I'm trying to do is get the data for the report below.
The right now the problem is that all the status are grouped into one row. I tried the below SQL to get the data as needed but it gives out an error that the syntax is not correct.
CREATE DEFINER=`root`#`localhost` PROCEDURE `TxCountByDatePerod`(IN `fromdate` DATE, IN `todate` DATE, IN `department` VARCHAR(20), IN `status` VARCHAR(10), IN `ipg` VARCHAR(20))
SELECT d.dept_name,
s.service_name,
COUNT(*) AS all_row_count,
IF(t.tx_status = 1,
SELECT COUNT(*)
FROM transaction_detail
WHERE tx_status = 1) AS successful_tx_count
FROM department AS d
INNER JOIN service s ON s.dept_id=d.dept_id
INNER JOIN transaction_detail t ON s.dept_id=t.dept_id
AND s.service_id=t.service_id
WHERE t.tx_begin_date BETWEEN fromdate AND todate
AND (t.dept_id = department
OR department ='null')
AND (t.tx_status = status
OR status ='null')
AND (t.pg_name = ipg
OR ipg ='null')
GROUP BY t.dept_id,
t.service_id
Please see the added line below.
IF(t.tx_status = 1, SELECT COUNT(*) FROM transaction_detail WHERE tx_status = 1) AS successful_tx_count is added to the SP.
How can I fix this?
the correct syntax for using an IF statement with a select is like this
CREATE DEFINER=`root`#`localhost` PROCEDURE `TxCountByDatePerod`(IN `fromdate` DATE, IN `todate` DATE, IN `department` VARCHAR(20), IN `status` VARCHAR(10), IN `ipg` VARCHAR(20))
SELECT d.dept_name,
s.service_name,
COUNT(*) AS all_row_count,
IF(t.tx_status = 1,
(SELECT COUNT(*) FROM transaction_detail WHERE tx_status = 1),
0
) AS successful_tx_count
FROM department AS d
INNER JOIN service s ON s.dept_id=d.dept_id
INNER JOIN transaction_detail t ON s.dept_id=t.dept_id
AND s.service_id=t.service_id
WHERE t.tx_begin_date BETWEEN fromdate AND todate
AND (t.dept_id = department
OR department ='null')
AND (t.tx_status = status
OR status ='null')
AND (t.pg_name = ipg
OR ipg ='null')
GROUP BY t.dept_id,
t.service_id
NOTE:
I just put in a default 0 value since you are doing a count but you can change that
IF(t.tx_status = 1
,(SELECT COUNT(*) FROM transaction_detail WHERE tx_status = 1)
,0 -- # -- this can be anything.. you could put in NULL or whatever else if you want
) AS successful_tx_count
you can also use a user defined variable and plug that in like so
SET #a := (SELECT COUNT(*) FROM transaction_detail WHERE tx_status = 1);
and then plug the #a variable in your if statement like so
if(t.tx_status = 1, #a, 0)
See this ARTICLE for more details on the if function
This is your select statement:
SELECT d.dept_name,
s.service_name,
COUNT(*) AS all_row_count,
IF(t.tx_status = 1,
SELECT COUNT(*)
FROM transaction_detail
WHERE tx_status = 1) AS successful_tx_count
Why are you using a subquery here? I suspect that you want the "successful" count to match the same conditions (from the where clause) as the count(*). So, just use conditional aggregation:
SELECT d.dept_name,
s.service_name,
COUNT(*) AS all_row_count,
SUM(t.tx_status = 1) AS successful_tx_count
Related
Here i have table attendances
I need result as shown below
How can i achieve this in mysql without using any programming language
Sql File is Attendances.sql
We can try a pivot query approach, aggregating by user and date:
SELECT
user_id,
DATE(date_time) AS date,
TIMESTAMPDIFF(MINUTE,
MAX(CASE WHEN status = 'IN' THEN date_time END),
MAX(CASE WHEN status = 'OUT' THEN date_time END)) / 60.0 AS hours
FROM yourTable
GROUP BY
user_id,
DATE(date_time);
The caveats of this answer are many. It assumes that there would be only one IN and OUT entry, per user, per day. If a period could cross over dates, then my answer might not generate correct results. Also, if an IN or OUT value be missing, then NULL would be reported for the hours value.
I have Achieve it my self by creating a mysql function and view
Mysql View
CREATE OR REPLACE VIEW `view_attendances` AS
SELECT
`a`.`id` AS `a1_id`,
`a`.`user_id` AS `user_id`,
CAST(`a`.`date_time` AS DATE) AS `date`,
`a`.`date_time` AS `in`,
`a2`.`id` AS `a2_id`,
`a2`.`date_time` AS `out`,
(TIMESTAMPDIFF(SECOND,
`a`.`date_time`,
`a2`.`date_time`) / 3600) AS `hours`
FROM
(`attendances` `a`
JOIN `attendances` `a2` ON (((`a`.`is_confirm` = 1)
AND (`a`.`status` = 'IN')
AND (`a2`.`id` = FN_NEXT_OUT_ATTENDANCE_ID(`a`.`user_id`, `a`.`date_time`, `a`.`status`))
AND (a2.status = 'OUT')
AND (CAST(`a`.`date_time` AS DATE) = CAST(`a2`.`date_time` AS DATE)))))
Mysql Function
CREATE FUNCTION `fn_next_out_attendance_id`( _user_id INT, _attendance_date_time DATETIME, _status VARCHAR(10) ) RETURNS int(11)
BEGIN
DECLARE _id INT(11);
SELECT
id INTO _id
FROM
attendances
WHERE
is_confirm = 1
AND user_id = _user_id
AND date_time > _attendance_date_time
AND `status` <> _status
ORDER BY
date_time ASC LIMIT 1 ;
RETURN if (_id IS NULL, 0, _id);
END
Assume I have 3 tables: Animal, CareTaker, and Apppointment. Schema, with some data like so:
Create Table Animal (Id int identity, Name varchar(25))
Create Table CareTaker(Id int identity, Name varchar(50))
Create Table Appointments(Id int identity, AnimalId int, CareTakerId int, AppointmentDate DateTime, BookingDate DateTime)
Insert into Animal(Name) Values('Ghost'), ('Nymeria'), ('Greywind'), ('Summer')
Insert into CareTaker(Name) Values ('Jon'), ('Arya'), ('Rob'), ('Bran')
Insert into Appointments(AnimalId, CareTakerId, AppointmentDate, BookingDate) Values
(1, 1, GETDATE() + 7, GetDate()), -- Ghost cared by Jon
(1, 2, GETDATE() + 6, GetDate()), -- Ghost cared by Arya
(4, 3, GETDATE() + 8, GetDate()) -- Summer cared by Rob
I want to select only 3 caretakers for each animal as a columns. Something like this:
I don't care about other appointments, just the next three, for each animal. If there aren't three appointments, it can be blank / null.
I'm quite confused about how to do this.
I tried it with Sub queries, something like so:
select Name,
-- Care Taker 1
(Select Top 1 C.Name
From Appointments A
Join CareTaker C on C.Id = A.CareTakerId
Where A.AppointmentDate > GETDATE()
And A.AnimalId = Animal.Id
Order By AppointmentDate) As CareTaker1,
-- Appointment Date 1
(Select Top 1 AppointmentDate
From Appointments
Where AppointmentDate > GETDATE()
And AnimalId = Animal.Id
Order By AppointmentDate) As AppointmentDate1
From Animal
But for the second caretaker, I would have to go second level select on where clause to exclude the id from top 1 (because not sure how else to get second row), something like select top 1 after excluding first row id; where first row id is (select top 1) situtation.
Anyhow, that doesn't look like a great way to do this.
How can I get the desired output please?
You can get all the information in rows using:
select an.name as animal, ct.name as caretaker, a.appointmentdate
from appointments a join
animals an
on a.id = an.animalid join
caretaker c
on a.caretakerid = c.id;
Then, you basically want to pivot this. One method uses the pivot keyword. Another conditional aggregation. I prefer the latter. For either, you need a pivot column, which is provided using row_number():
select animal,
max(case when seqnum = 1 then caretaker end) as caretaker1,
max(case when seqnum = 1 then appointmentdate end) as appointmentdate1,
max(case when seqnum = 2 then caretaker end) as caretaker2,
max(case when seqnum = 2 then appointmentdate end) as appointmentdate2,
max(case when seqnum = 3 then caretaker end) as caretaker3,
max(case when seqnum = 3 then appointmentdate end) as appointmentdate3
from (select an.name as animal, ct.name as caretaker, a.appointmentdate,
row_number() over (partition by an.id order by a.appointmentdate) as seqnum
from appointments a join
animals an
on a.id = an.animalid join
caretaker c
on a.caretakerid = c.id
) a
group by animal;
DECLARE #aracct VARCHAR(12)
SET #aracct = '49920813'
DECLARE #item_no VARCHAR(12)
SET #item_no = '2319'
SELECT tran_id, * FROM transactions
WHERE account = #aracct AND item_no = #item_no
/*-----------Use if removing certain items from a check-----------*/
CREATE TABLE itemtable (items VARCHAR(6), account VARCHAR(12))
INSERT INTO itemtable VALUES ('2299', #aracct)
INSERT INTO itemtable VALUES ('2300', #aracct)
--INSERT INTO #itemtable VALUES ('')
DECLARE #tran_id_pmt VARCHAR(6)
SET #tran_id_pmt = '209413'
SELECT I1.result - I2.result AS 'final result'
FROM (SELECT SUM(amt_paid) AS result
FROM transactions
WHERE account = #aracct AND item_no = #item_no) AS I1
JOIN (SELECT SUM(amt_paid) AS result
FROM transactions
WHERE account = #aracct AND item_no = (SELECT items FROM itemtable)) AS I2
ON (I1.account = I2.account)
Trying to get the sum 2 select statements and then sum those 2 statements, but I keep getting an error. Any ideas?
You need include account on your subquery so you can use it on the ON condition
SELECT I1.result - I2.result AS 'final result'
FROM (SELECT account, SUM(amt_paid) AS result
^^^^^^^
FROM transactions
WHERE account = #aracct AND item_no = #item_no
GROUP BY account) AS I1
^^^^^^^^^^^^^^^^
JOIN (SELECT account, SUM(amt_paid) AS result
FROM transactions
WHERE account = #aracct AND item_no = (SELECT items FROM itemtable)
GROUP BY account) AS I2
ON (I1.account = I2.account)
^^^^^^^^
You could also solve it with a conditional aggregation.
SELECT SUM(CASE WHEN item_no = #item_no THEN amt_paid
ELSE 0
END)
-
SUM(CASE WHEN item_no IN (SELECT items FROM itemtable) THEN amt_paid
ELSE 0
END) AS 'final result'
FROM Transactions
WHERE account = #aracct
I have the following statement, which produces this error. Apparently the alias "R" in the first line of the select statement is not declared, but it's right there at the end of the statement. Anyone have any ideas?
#1327 - Undeclared variable: R
CREATE PROCEDURE `get_series_completion`(IN team1_id INT, IN team2_id INT, IN round_shortname VARCHAR(5))
BEGIN
DECLARE num_games_total, num_games_played INT;
SELECT COUNT(*) INTO num_games_played, R.games_per_series INTO num_games_total
FROM (
SELECT game_id
FROM team_games
WHERE team_id IN (team1_id, team2_id)
GROUP BY game_id
HAVING COUNT(*) = 2
AND SUM(standing = 0) = 0
) AS S, rounds AS R ON R.round_shortname = round_shortname;
SELECT num_games_played/num_games_total;
END$$
You should select all columns before into clause, Your query should be:
SELECT COUNT(*), R.games_per_series INTO num_games_played, num_games_total
FROM (
SELECT game_id
FROM team_games
WHERE team_id IN (team1_id, team2_id)
GROUP BY game_id
HAVING COUNT(*) = 2
AND SUM(standing = 0) = 0
) AS S
LEFT OUTER JOIN rounds AS R ON R.round_shortname = round_shortname;
or
SELECT COUNT(*), R.games_per_series INTO num_games_played, num_games_total
FROM (
SELECT game_id
FROM team_games
WHERE team_id IN (team1_id, team2_id)
GROUP BY game_id
HAVING COUNT(*) = 2
AND SUM(standing = 0) = 0
) AS S, rounds AS R
WHERE R.round_shortname(+) = round_shortname; -- implicit left outer join
I am attempting to create a function that incorporates multiple 'With' Statements.
My original Query is:
WITH EmpCount as
(
SELECT job.EmployeeID, assign.PropertyID FROM EmployeeJobstatus job
JOIN Assignment assign ON job.JobID = assign.ID GROUP by job.EmployeeID,
assign.PropertyID
),
NoDup as
(
SELECT EmployeeID, Count(employeeID) as NO from EmpCount
Group by EmployeeID HAVING count(Employeeid) > 1
)
SELECT prop.Name, job.EmployeeID, Emp.EmpID, Emp.Name
from EmployeeJobStatus job
JOIN Assignment assign ON assign.id = job.jobid
JOIN Property prop ON prop.ID = assign.PropertyID
JOIN Employee emp on emp.ID = job.EmployeeID
WHERE EmployeeID IN (SELECT EmployeeID from NoDup)
GROUP By prop.Name, EmployeeID, emp.EmpID,Emp.Name
Order BY EmployeeID
This returns:
Name EmployeeID EmpID Name
Property 1 23 1286333 LastNameRemoved1, Rachel A
Property 2 23 1286333 LastNameRemoved1, Rachel A
Property 2 76 1268329 LastNameRemoved2, Tamer A
Property 1 76 1268329 LastNameRemoved2, Tamer A
Property 3 135 1411933 LastNameRemoved3, Sarah E
Property 1 135 1411933 LastNameRemoved2, Sarah E
My function needs to return a 'Y' or a 'N' depending on whether or not they were cross property, utilizing the fields in EmployeeJobStatus StartDate and EndDate.
I have only started my function, as I have no idea what to do next.
CREATE function dbo.IsEmployeeCrossPropertyOnDate
(#EmpID int, #AsOfDate datetime)
RETURNS INT AS
Any help, or a nudge in the right direction would be much appreciated. Thank you.
How about this...
CREATE FUNCTION dbo.IsEmployeeCrossPropertyOnDate
(
#empid int
, #asofdate datetime
)
RETURNS TABLE
AS
RETURN
SELECT
CASE MIN(a.propertyid)
WHEN MAX(a.propertyid)
THEN 'N'
ELSE 'Y'
END CrossProp
FROM
EmployeeJobStatus j
JOIN
Assignment a
ON a.id = j.jobid
WHERE
j.employee = #empid
AND j.startdate <= #AsOfDate
AND j.endDate > #AsOfDate
GROUP BY
j.employeeid;
EDIT:
You'd probably outer apply it, rather than cross. Nulls would then be employees with no assignments at the point in time.
EDIT 2:
Since you asked, below is a scalar function. Just be careful of scalars. Depending on where you use it in a query, the BEGIN END block can fool the optimizer into going RBAR.
CREATE FUNCTION dbo.IsEmployeeCrossPropertyOnDate
(
#empid int
, #asofdate datetime
)
RETURNS CHAR(1)
AS
BEGIN
DECLARE #res CHAR(1) = 'N';
SELECT #res =
CASE MIN(a.propertyid)
WHEN MAX(a.propertyid)
THEN 'N'
ELSE 'Y'
END
FROM
EmployeeJobStatus j
JOIN
Assignment a
ON a.id = j.jobid
WHERE
j.employee = #empid
AND j.startdate <= #AsOfDate
AND j.endDate > #AsOfDate
GROUP BY
j.employeeid;
RETURN #res;
END;