Mysql how to generate row index (rank) in SELECT? - mysql

As result of mysql query, I have this table:
orders | customer |
1 | A |
1 | A |
1 | B |
1 | B |
1 | B |
Using mysql only, I need to create a column with index oder ocurrence to each customer to get this table:
orders | customer | index
1 | A | 1
1 | A | 2
1 | B | 1
1 | B | 2
1 | B | 3
I try to use this:
set #i=1;
while #i<99999 do
select
count(order_id) as 'orders',
customer_id as 'customer',
#i as 'index'
from
orders
set #i= #i+1;
end while;
But I get an error of statement. Sorry, I have no more idea how to do it. Any idea will be appreciated.

The standard way of doing this with MySQL 8.0 is to use a windowing function:
SELECT orders, customer,
ROW_NUMBER() OVER (PARTITION BY customer) AS `index`
FROM orders
Prior to MySQL 8.0, you can do tricks with inline user variables.
SET #i = 0, #c = '';
SELECT
orders,
IF(#c=customer, #i:=#i+1, #i:=1) AS `index`,
#c:=customer AS customer
FROM orders
ORDER BY customer;
Unfortunately, this needs the customer column to be after the index column.

SELECT
orders.* ,
customerOrderCount ,
IF(#i > 1, #i:= #i - 1, #i:=customerOrdercount) AS orderIndex
FROM (SELECT * FROM orders ORDER BY customer ASC ) AS orders
JOIN (SELECT customer, count(*) AS customerOrderCount FROM orders GROUP BY
customer) counts USING (customer)
ORDER BY customer ASC, orderIndex;

Related

Calculate date difference from previous row of each unique ID in MySQL

I am a SQL beginner and am learning the ropes of querying. I'm trying to find the date difference between purchases by the same customer. I have a dataset that looks like this:
ID | Purchase_Date
==================
1 | 08/10/2017
------------------
1 | 08/11/2017
------------------
1 | 08/17/2017
------------------
2 | 08/09/2017
------------------
3 | 08/08/2017
------------------
3 | 08/10/2017
I want to have a column that shows the difference in days for each unique customer purchase, so that the output will look like this:
ID | Purchase_Date | Difference
===============================
1 | 08/10/2017 | NULL
-------------------------------
1 | 08/11/2017 | 1
-------------------------------
1 | 08/17/2017 | 6
-------------------------------
2 | 08/09/2017 | NULL
-------------------------------
3 | 08/08/2017 | NULL
-------------------------------
3 | 08/10/2017 | 2
What would be the best way to go about this using a MySQL query?
Not so hard, just use a subquery to find previous purchase for each existing purchase for the customer, and self-join to that record.
Select t.id, t.PurchaseDate, p.Purchase_date,
DATEDIFF(t.PurchaseDate, p.Purchase_date) Difference
From myTable t -- t for This purchase record
left join myTable p -- p for Previous purchase record
on p.id = t.Id
and p.purchase_date =
(Select Max(purchase_date)
from mytable
where id = t.id
and purchase_date <
t.purchaseDate)
This is rather tricky in MySQL. Probably the best way to learn if you are a beginning is the correlated subquery method:
select t.*, datediff(purchase_date, prev_purchase_date) as diff
from (select t.*,
(select t2.purchase_date
from t t2
where t2.id = t.id and
t2.purchase_date < t.purchase_date
order by t2.purchase_date desc
limit 1
) as prev_purchase_date
from t
) t;
Performance should be okay if you have an index on (id, purchase_date).
It is possible to solve it not using dependent subquery
SELECT yt.id, create_date, NULLIF(yt.create_date - tm.min_create_date, 0)
FROM your_table yt
JOIN
(
SELECT id, MIN(create_date) min_create_date
FROM your_table
GROUP BY id
) tm ON tm.id = yt.id
sqlfiddle demo

MySql Database query for selecting random from each category?

Table Name : Employee
+------+------+
| name | dept |
+------+------+
| ABC | 1 |
| BCA | 1 |
| CYZ | 2 |
| CYZ | 1 |
| n... | n... |
+------+------+
Table Name : Department
+----+-----------+
| id | dept_name |
+----+-----------+
| 1 | YYY |
| 2 | ZZZ |
| 3 | DDD |
+----+-----------+
I've to select 25 random entries against each dept_name from table Employees.
For eg. In table Department there are 10 entries with unique id.
so the result query will return 25(random) * 10 = 250 rows.
so far I'm working out this query but something is wrong with it.
Select * from Employee where dept in (Select id from Department) RAND;
Probably something like below:
SELECT d.id, GROUP_CONCAT(name) employee_list FROM Department d JOIN (
SELECT name FROM EMPLOYEE e WHERE d.id = e.dept ORDER BY RAND() LIMIT 25
) v
GROUP BY d.id
LIMIT 10
One option is to GROUP_CONCAT employees per department and then limit the list to 25 entries using SUBSTRING_INDEX(). Then join the limited values with employee table, like this:
SELECT e.* FROM Employee e
JOIN (
SELECT SUBSTRING_INDEX(GROUP_CONCAT(eid ORDER BY RAND()), ',', 25) EmpList
FROM Employee
GROUP BY dept
) t
ON FIND_IN_SET(e.eid, t.EmpList)
Working Fiddle: http://sqlfiddle.com/#!9/1bd04/1
Note: GROUP_CONCAT has a default limit of 1024 which can be increased.
Second option is to assign row numbers to all employees per department and then limit the result to 25 per department. This one does not uses group_concat.
SET #num := 0, #d_id := '';
SELECT eid, name, dept
FROM (
SELECT e.*,
#num := IF(#d_id = e.dept, #num + 1, 1) AS row_number,
#d_id := e.dept AS dummy
FROM (
SELECT * FROM Employee ORDER BY dept, RAND()
) e
) t
WHERE t.row_number <= 25
Working Fiddle: http://sqlfiddle.com/#!9/1bd04/22
Note: Both the fiddles above limits 2 employees per department for demo purposes.

How to calculate running total grouped by Order No

Trying to create a running total for orders in SQL Server 2008, similar to the below table (Order No & Order Total columns exist in my SQL Server table), tried using a recursive cte but my results were a running total for all orders, not grouped by order no. Any suggestions how to have the running total grouped by the order no? Thanks
---------------------------------------------------------
| Order No. | Order Total | Running Total for Order No |
---------------------------------------------------------
| 1 | $10,000 | $10,000 |
---------------------------------------------------------
| 1 | -$5,000 | $5,000 |
---------------------------------------------------------
| 1 | $3,000 | $8,000 |
---------------------------------------------------------
| 2 | $2,500 | $2,500 |
---------------------------------------------------------
| 2 | $5,000 | $7,500 |
---------------------------------------------------------
| 2 | $4,000 | $11,000 |
---------------------------------------------------------
I would do this is with an Instead of Insert Trigger. The trigger would subtract/add from the groups first value. Obviously this should of been done at the creation of the table but you could add it after you make table update.
Keep in mind in order for the below code to work, you would need a primary key on the Order table
CREATE TABLE Orders
(
id INT IDENTITY(0, 1) PRIMARY KEY
, orderNo INT
, orderTotal MONEY
, runningTotal MONEY
);
INSERT INTO Orders
VALUES
(1,10000,10000),
(1,-5000,5000),
(1,3000,8000),
(2,2500,2500),
(2,5000,7500),
(2,4000,11500);
GO
--CREATE TRIGGER
CREATE TRIGGER trg_RunningTotal ON Orders
INSTEAD OF INSERT
AS
BEGIN
DECLARE #PreviousTotal MONEY =
(
SELECT TOP 1
a.runningTotal
FROM Orders AS a
INNER JOIN INSERTED AS b ON a.orderNo = b.orderNo
WHERE a.orderno = b.Orderno
ORDER BY a.id DESC
);
INSERT INTO Orders
SELECT
orderno,
orderTotal,
(#PreviousTotal + orderTotal) AS runningTotal
FROM INSERTED;
END;
--Insert new record
INSERT INTO orders
VALUES
(1,1000,NULL);
--View newly added record
SELECT
*
FROM orders
WHERE orderno = 1;
You need to following query:
SELECT orderno,
SUM((CASE WHEN ISNUMERIC(ordertotal)=1
THEN CONVERT(MONEY,ordertotal) ELSE 0 END)
)
AS [Converted to Numeric]
FROM price group by orderno

MySQL - Order query and display one random row at the top

I have a table that looks like:
ID | TICKET PRICE | VIP
----------------------------
1 | $45.00 | 1
2 | $40.00 | 1
3 | $20.00 | 0
4 | $65.00 | 0
5 | $45.00 | 1
I need to query this table to order all rows by Price, but always show one random row which has a VIP=1 at the top. So for example, the query should return:
ID | TICKET PRICE | VIP
----------------------------
2 | $40.00 | 1
3 | $20.00 | 0
1 | $45.00 | 1
5 | $45.00 | 1
4 | $65.00 | 0
And when you refresh the page, row ID 5 may then become the first row, because it has a VIP=1.
I currently have my query looking like:
(SELECT * FROM tickets WHERE VIP=1 ORDER BY rand() LIMIT 1)
UNION
(SELECT * FROM tickets WHERE VIP=0 ORDER BY ticket_price ASC)
This issue with this is that it will only display one VIP row. How would I query this data properly?
Use order by. Here is one method:
select t.*
from (select t.*, (#rn := #rn + 1) as seqnum
from tickets t cross join
(select #rn := 0) params
order by vip desc, rand()
) t
order by (seqnum = 1) desc, price asc;
This uses the subquery to identify the one row to keep at the top. Then it uses this information for ordering in the outer query.
If your rows have a unique identifier, you could also do:
select t.*
from tickets t cross join
(select id from tickets where vip = 1 order by rand() limit 1) as t1
order by (t.id = t1.id) desc, price asc;

Deleting all but the most recent entry from single SQL table

I have a single SQL table that contains multiple entries for each customerID (some customerID's only have one entry which I want to keep). I need to remove all but the most recent entry per customerID, using the invoiceDate field as my marker.
So I need to go from this:
+------------+-------------+-----------+
| customerID | invoiceDate | invoiceID |
+------------+-------------+-----------+
| 1 | 1393995600 | xx |
| 1 | 1373688000 | xx |
| 1 | 1365220800 | xx |
| 2 | 1265220800 | xx |
| 2 | 1173688000 | xx |
| 3 | 1325330800 | xx |
+------------+-------------+-----------+
To this:
+------------+-------------+-----------+
| customerID | invoiceDate | invoiceID |
+------------+-------------+-----------+
| 1 | 1393995600 | xx |
| 2 | 1265220800 | xx |
| 3 | 1325330800 | xx |
+------------+-------------+-----------+
Any guidance would be greatly appreciated!
Write a query to select all the rows you want to delete:
SELECT * FROM t
WHERE invoiceDate NOT IN (
SELECT MAX(invoiceDate)
-- "FROM t AS t2" isn't supported by MySQL, see http://stackoverflow.com/a/14302701/227576
FROM (SELECT * FROM t) AS t2
WHERE t2.customerId = t.customerId
GROUP BY t2.customerId
)
This may take a long time on a big database.
If you're satisfied, change the query to a DELETE statement:
DELETE FROM t
WHERE invoiceDate NOT IN (
SELECT MAX(invoiceDate)
-- "FROM t AS t2" isn't supported by MySQL, see http://stackoverflow.com/a/14302701/227576
FROM (SELECT * FROM t) AS t2
WHERE t2.customerId = t.customerId
GROUP BY t2.customerId
)
See http://sqlfiddle.com/#!9/6e031/1
If you have multiple rows whose date is the most recent for the same customer, you would have to look for duplicates and decide which one you want to keep yourself. For instance, look at customerId 2 on the SQL fiddle link above.
Try out this one
with todelete as
(
select
CustomerId, InvoiceId, InvoiceDate, Row_Number() over (partition by CustomerId order by InvoiceDate desc) as Count
from DeleteDuplicate
)
delete from todelete
where count > 1
Let us asume that the table name is transaction_table.
create table test1 AS
select * from (
select * from transaction_table order by customerID, invoiceDate desc) temp
group by customerID
You will have the output data in test1 table.
delete from ex_4 where
rowid in
(select rowid
from ex_4 a
where to_date(invoicedate,'DDMMYYYY') = (select max(to_date(invoicedate,'DDMMYYYY')) from ex_4 b where a.customerid != b.customerid))
This is how it will be done in oracle.This query will delete all but most recently added row.Looking at your table structure i am assuming that the invoicedate column is varchar2 type so converting it to date used to_date function here