Want to count each individual employee's presents and absents - mysql

i've created a employee attendance table having 3 columns emp_name, emp_present & date,
there is no primary key in mytable
i'm using this query to get total presents of employees
SELECT `emp_name`, `emp_present`,(select sum(emp_present='present')
from empattendance) as 'total presents', `date` FROM `empattendance` where date=curdate()
but i want to get total presents and 'total absents' of each indiviual shown with them in total presents and 'total absents' column respectively. I'm a newbie in database and don't have much knowledge about how to sort this problem.....

You can use conditional aggregation, to get the count of rows where employee was present and the count of rows where employee was absent like this:
SELECT emp_name,
SUM(emp_present = 'present') AS numPresence,
SUM(emp_present = 'absent') AS numAbsence
FROM employees
GROUP BY emp_name;
EDIT:
If you want a running total of presence and absences, you'll need to use a variable:
SET #present := 0;
SET #absent := 0;
SELECT emp_name, dateColumn,
IF(emp_present = 'present', #present := #present + 1, #present) AS numPresence
IF(emp_present = 'absent', #absent := #absent+ 1, #absent) AS numAbsense
FROM myTable
ORDER BY dateColumn;

Related

Mysql - Accumulatively count the total on a row by row basis

I'm trying in MySql to count the number of users created each day and then get an accumulative figure on a row by row basis. I have followed other suggestions on here, but I cannot seem to get the accumulation to be correct.
The problem is that it keeps counting from the base number of 200 and not taking account of previous rows.
Where was I would expect it to return
My Sql is as follows;
SELECT day(created_at), count(*), (#something := #something+count(*)) as value
FROM myTable
CROSS JOIN (SELECT #something := 200) r
GROUP BY day(created_at);
To create the table and populate it you can use;
CREATE TABLE myTable (
id INT AUTO_INCREMENT,
created_at DATETIME,
PRIMARY KEY (id)
);
INSERT INTO myTable (created_at)
VALUES ('2018-04-01'),
('2018-04-01'),
('2018-04-01'),
('2018-04-01'),
('2018-04-02'),
('2018-04-02'),
('2018-04-02'),
('2018-04-03'),
('2018-04-03');
You can view this on SqlFiddle.
Use a subquery:
SELECT day, cnt, (#s := #s + cnt)
FROM (SELECT day(created_at) as day, count(*) as cnt
FROM myTable
GROUP BY day(created_at)
) d CROSS JOIN
(SELECT #s := 0) r;
GROUP BY and variables have not worked together for a long time. In more recent versions, ORDER BY also needs a subquery.

generate serial number which resets for a particular condition using mysql query

I have a table named browsekot in mysql database.This table contains menu items that have been ordered from different outlets(restaurants) along with their quantites and price during dine-in. I need to generate a report.
On using below query:
rec.Open "select Outlet,ItemName,id,sum(Quantity) as Quantity, sum(Value) as Value,#i:= #i + 1 as result from (SELECT #i := 0) h , browsekot group by ItemName,Outlet order by #i ", adoconn
gives my this Output:
Sr.No.....Outlet.....Name
1.............Taj...........x
2.............Taj...........y
3.............Mez..........t
4.............Mez..........z
But i want to reset the count #i for each outlet and want my output to be:
Sr.No.....Outlet.....Name
1.............Taj...........x
2.............Taj...........y
1.............Mez..........t
2.............Mez..........z
I want to reset the count in the above query itself as i will be using this query later with SHAPE Command for displaying in datareport. how do i change the above query to reset the count for each outlet?
Use the following SQL:
select Outlet,ItemName,id, Quantity, `Value`,
#i:= IF(Outlet = #last_outlet, #i + 1, 1) as result,
#last_outlet := Outlet
from (SELECT #i := 0, #last_outlet := NULL) h
JOIN (SELECT Outlet, ItemName, id, SUM(Quantity) AS Quantity, SUM(`Value`) as `Value`
FROM browsekot
GROUP BY ItemName,Outlet
ORDER BY Outlet) i
FIDDLE

Complicated Query

I'm not sure if the following can be done using a mere select statement, but I have two tables (truncated with the data necessary to the problem).
Inventory Item
id int (PRIMARY)
quantity int
Stock - Contains changes in the stock of the inventory item (stock history)
id int (PRIMARY)
inventory_item_id int (FOREIGN KEY)
quantity int
created datetime
The quantity in stock is the change in stock, while the quantity in inventory item is the current quantity of that item
EVERYTHING IN THE running COLUMN WILL RETURN 0
SELECT
inventory_item.id,
(inventory_item.quantity - SUM(stock.quantity)) AS running
FROM
stock
JOIN
inventory_item ON stock.inventory_item_id = inventory_item.id
GROUP BY inventory_item.id
THE QUESTION
Now, what I would like to know is: Is it possible to select all of the dates in the stock table where the running quantity of the inventory_item ever becomes zero using a SELECT?
I know this can be done programmatically by simply selecting all of the stock data in one item, and subtracting the stock quantity individually from the current inventory item quantity, which will get the quantity before the change in stock happened. Can I do this with a SELECT?
(Updated) Assuming there will never be more than one record for a given combination of inventory_item_id and created, try:
SELECT i.id,
s.created,
i.quantity - COALESCE(SUM(s2.quantity),0) AS running
FROM inventory_item i
JOIN stock s ON s.inventory_item_id = i.id
LEFT JOIN stock s2 ON s2.inventory_item_id = i.id and s.created < s2.created
GROUP BY i.id, s.created
HAVING running=0
My take on it:
select
inventory_item_id `item`,
created `when`
from
(select
#total := CASE WHEN #curr <> inventory_item_id
THEN quantity
ELSE #total+quantity END as running_total,
inventory_item_id,
created,
#curr := inventory_item_id
from
(select #total := 0) a
(select #curr := -1) b
(select inventory_item_id, created, quantity from stock order by inventory_item_id, created asc) c
) running_total
where running_total.running_total = 0;
This one has the relative advantage of having to give only one pass to the stock table. Depending on the size and the indexes on it that may or may not be a good thing.
The most logical way to do this is with a cumulative sum. But, MySQL doesn't support that.
The clearest approach, in my opinion, is to use a correlated subquery to get the running quantity. Then it is a simple matter of a where clause to select where it is 0:
select i.*
from (select i.*,
(select SUM(i2.inventory)
from inventory i2
where i2.inventory_item_id = i.inventory_item_id and
i2.created <= i.created
) as RunningQuantity
from inventory i
) i
where RunningQuantity = 0;
I had a response similar based on a running total to be flagged found here...
You can do with MySQL #variables, but the data needs to be pre-queried and ordered by the data of activity... then set a flag on each row that causes the negative and keep only those. Something like
select
PreQuery.*
from
( select
s.id,
s.created,
#runBal := if( s.id = #lastID, #runBal - quantity, #i.quantity ) as CurBal,
#lastID := s.id as IDToCompareNextEntry
from
stock s
join inventory_item ii
on s.inventory_item_id = ii.id,
(select #lastID := -1,
#runBal := 0 ) sqlvars
order by
s.id,
s.created DESC ) PreQuery
where
PreQuery.CurBal < 0
This way, for each inventory item, it works backwards by created date (order by the created descending per ID). So, when the inventory ID changes, look to the inventory table "Quantity" field to START the tally of used stock down. If same ID as the last record processed, just use the running balance and subtract out the quantity of that stock entry.
I believe this is a simple approach to this.
SELECT inventory_item.id, stock.created
FROM inventory_item
JOIN stock ON stock.inventory_item_id = inventory_item.id
WHERE (SELECT SUM(quantity) FROM stock WHERE created <= stock.created) = 0

Get a query to list the records that are on and in between the start and the end values of a particular column for the same Id

There is a table with the columns :
USE 'table';
insert into person values
('11','xxx','1976-05-10','p1'),
('11','xxx ','1976-06-11','p1'),
('11','xxx ','1976-07-21','p2'),
('11','xxx ','1976-08-31','p2'),
Can anyone suggest me a query to get the start and the end date of the person with respect to the place he changed chronologically.
The query I wrote
SELECT PId,Name,min(Start_Date) as sdt, max(Start_Date) as edt, place
from **
group by Place;
only gives me the first two rows of my answer. Can anyone suggest the query??
This isn't pretty, and performance might be horrible, but at least it works:
select min(sdt), edt, place
from (
select A.Start_Date sdt, max(B.Start_Date) edt, A.place
from person A
inner join person B on A.place = B.place
and A.Start_Date <= B.Start_Date
left join person C on A.place != C.place
and A.Start_Date < C.Start_Date
and C.Start_Date < B.Start_Date
where C.place is null
group by A.Start_Date, A.place
) X
group by edt, place
The idea is that A and B represent all pairs of rows. C will be any row in between these two which has a different place. So after the C.place is null restriction, we know that A and B belong to the same range, i.e. a group of rows for one place with no other place in between them in chronological order. From all these pairs, we want to identify those with maximal range, those which encompass all others. We do so using two nested group by queries. The inner one will choose the maximal end date for every possible start date, whereas the outer one will choose the minimal start date for every possible end date. The result are maximal ranges of chronologically subsequent rows describing the same place.
This can be achived by:
SELECT Id, PId,
MIN(Start_Date) AS sdt,
MAX(Start_Date) as edt,
IF(`place` <> #var_place_prev, (#var_rank:= #var_rank + 1), #var_rank) AS rank,
(#var_place_prev := `place`) AS `place`
FROM person, (SELECT #var_rank := 0, #var_place_prev := "") dummy
GROUP BY rank, Place;
Example: SQLFiddle
If you want records to be ordered by ID then:
SELECT Id, PId,
MIN(Start_Date) AS sdt,
MAX(Start_Date) as edt,
`place`
FROM(
SELECT Id, PId,
Start_Date
IF(`place` <> #var_place_prev,(#var_rank:= #var_rank + 1),#var_rank) AS rank,
(#var_place_prev := `place`) AS `place`
FROM person, (SELECT #var_rank := 0, #var_place_prev := "") dummy
ORDER BY ID ASC
) a
GROUP BY rank, Place;

mysql get row number of a specific result

My website allows users to record bids. Each bid is saved individually and associated to a user Id. A user can have many bids which are used to add up to one overall bid which is displayed upon the site.
What I am trying to do in sql is return the position a users overall bid is from a result set.
The sql I am using is below but problems arise when I use the group by command - the ordering seems to revert back to the default db order rather than by the sum of a users bid amounts:
SET #rowcount = 0;
SELECT rowCount, userId FROM (
SELECT userId, #rowcount := #rowcount + 1 as rowCount, sum(amount) as amount FROM bids group by userId order by amount desc
) t when product = xxxxx
appreciate if anyone knows if this is possible?
You need to move rowcount incrementation out of subquery. And put your WHERE condition inside, otherwise your subquery will sum bids on all products for a given user.
SET #rowcount = 0;
SELECT #rowCount:=#rowcount+1 as rowcount, userId, amount FROM
(
SELECT userId, sum(amount) as amount
FROM bids
WHERE product = xxxxx
GROUP BY userId
ORDER BY amount DESC
) t
Have you already tried?
SET #rowcount = 0;
SELECT rowCount, userId FROM (
SELECT userId, #rowcount := #rowcount + 1 as rowCount from
(select sum(amount) as amount, userId FROM bids group by userId) s
order by s.amount desc
) t where product = xxxxx
if I understand it correctly you could try something like this:
SELECT #rownum:=#rownum+1 ‘rank’, userId FROM (SELECT product, userId, sum(amount) AS amount FROM bids GROUP BY userId) t, (SELECT #rownum:=0) r WHERE t.product = xxxx ORDER BY t.amount DESC;