Duplicate Records on Insert - mysql

I have a database table that records which site_id is being used for each user_id. I am getting duplicates everytime I save the settings. I want it so I can query the table for user_id = 99 and see which site_id is associated with that user_id.
SQL:
$sql = "INSERT INTO user_sites (rooftop_id, site_id, created_at, active)
VALUES ('$rooftop_id','$key', now(), '$value')
ON DUPLICATE KEY
UPDATE updated_at = IF(active=VALUES(active),updated_at,NOW()),
active = VALUES(active)
";
Current Output:
+----+---------+---------+--------+
| id | user_id | site_id | active |
+----+---------+---------+--------+
| 1 | 99 | 30 | 1 |
| 2 | 99 | 20 | 1 |
| 3 | 99 | 40 | 1 |
| 4 | 32 | 30 | 1 |
| 5 | 32 | 20 | 1 |
| 6 | 32 | 40 | 1 |
| 7 | 32 | 30 | 0 |
| 8 | 32 | 20 | 0 |
| 9 | 32 | 40 | 0 |
+----+---------+---------+--------+
Desired Output:
+----+---------+---------+--------+
| id | user_id | site_id | active |
+----+---------+---------+--------+
| 1 | 99 | 30 | 1 |
| 2 | 99 | 20 | 1 |
| 3 | 99 | 40 | 1 |
| 4 | 32 | 30 | 0 |
| 5 | 32 | 20 | 0 |
| 6 | 32 | 40 | 0 |
+----+---------+---------+--------+

Have you defined a unique key on the table? For what you want, I think it is:
create unique index admin_sites_userid_siteid on admin_sites(user_id, site_id);
This is then used to define the when an insert matches an existing row.

Related

calculate unit in stock of a product

This is the question:
To find
units in stock of a product, use the InventoryTransactions table, find total quantity
purchased and subtract quantities sold and on hold.
The Inventory Transaction table:
This are the transaction type for each transaction id:
transaction id 1 = purchased, trasaction id 2 = sold, transaction id 3 = on hold
+----+-----------+
| ID | TypeName |
+----+-----------+
| 1 | Purchased |
| 2 | Sold |
| 3 | On Hold |
| 4 | Waste |
+----+-----------+
4 rows in set (0.00 sec)
The product id, quantity, and type of transaction:
SELECT productid, quantity, transactiontype FROM Inventory_Transactions ORDER BY productid;
+-----------+----------+-----------------+
| productid | quantity | transactiontype |
+-----------+----------+-----------------+
| 1 | 40 | 1 |
| 1 | 15 | 2 |
| 1 | 25 | 3 |
| 3 | 100 | 1 |
| 3 | 50 | 2 |
| 4 | 40 | 1 |
| 4 | 10 | 2 |
| 4 | 30 | 2 |
| 5 | 40 | 1 |
| 5 | 25 | 2 |
| 6 | 100 | 1 |
| 6 | 10 | 2 |
| 6 | 90 | 2 |
| 7 | 40 | 1 |
| 7 | 10 | 2 |
| 7 | 30 | 2 |
| 8 | 40 | 1 |
| 8 | 17 | 2 |
| 8 | 25 | 1 |
| 8 | 25 | 2 |
| 8 | 20 | 2 |
| 8 | 3 | 2 |
| 14 | 40 | 1 |
| 17 | 40 | 1 |
| 17 | 40 | 2 |
| 19 | 20 | 1 |
| 19 | 20 | 2 |
| 19 | 30 | 1 |
| 19 | 30 | 2 |
| 19 | 25 | 1 |
| 19 | 10 | 2 |
| 19 | 10 | 1 |
| 19 | 25 | 2 |
| 20 | 40 | 1 |
| 20 | 40 | 2 |
| 21 | 20 | 1 |
| 21 | 20 | 2 |
| 34 | 60 | 1 |
| 34 | 100 | 1 |
| 34 | 100 | 2 |
| 34 | 12 | 3 |
| 34 | 10 | 3 |
| 34 | 1 | 3 |
| 34 | 50 | 1 |
| 34 | 300 | 1 |
| 34 | 300 | 2 |
| 34 | 87 | 2 |
| 40 | 120 | 1 |
| 40 | 50 | 2 |
| 40 | 30 | 2 |
| 40 | 40 | 2 |
| 41 | 40 | 1 |
| 41 | 200 | 1 |
| 41 | 200 | 2 |
| 41 | 30 | 2 |
| 41 | 50 | 1 |
| 41 | 50 | 2 |
| 41 | 10 | 2 |
| 43 | 100 | 1 |
| 43 | 20 | 2 |
| 43 | 300 | 1 |
| 43 | 300 | 2 |
| 43 | 25 | 3 |
| 43 | 250 | 1 |
| 43 | 300 | 3 |
| 43 | 5 | 2 |
| 48 | 100 | 1 |
| 48 | 10 | 2 |
| 48 | 100 | 1 |
| 48 | 100 | 2 |
| 48 | 10 | 2 |
| 48 | 40 | 2 |
| 48 | 40 | 2 |
| 51 | 40 | 1 |
| 51 | 10 | 2 |
| 51 | 30 | 2 |
| 52 | 100 | 1 |
| 52 | 40 | 2 |
| 56 | 120 | 1 |
| 56 | 110 | 3 |
| 57 | 80 | 1 |
| 57 | 100 | 1 |
| 57 | 100 | 2 |
| 65 | 40 | 1 |
| 66 | 80 | 1 |
| 72 | 40 | 1 |
| 72 | 50 | 1 |
| 72 | 50 | 2 |
| 72 | 40 | 2 |
| 74 | 20 | 1 |
| 74 | 20 | 2 |
| 77 | 60 | 1 |
| 80 | 75 | 1 |
| 80 | 30 | 2 |
| 80 | 10 | 2 |
| 80 | 20 | 3 |
| 80 | 15 | 2 |
| 81 | 125 | 1 |
| 81 | 200 | 1 |
| 81 | 200 | 2 |
| 81 | 50 | 3 |
| 81 | 25 | 3 |
+-----------+----------+-----------------+
102 rows in set (0.00 sec)
I will need to calculate unit of stock for each productid by SUM(quantity purchased) - SUM(quantity sold) - SUM(quantity on hold)
My take:
CREATE VIEW purchased AS
SELECT productid, SUM(quantity) quantity
FROM Inventory_Transactions
WHERE transactiontype = 1
GROUP BY productid
ORDER BY productid;
CREATE VIEW sold AS
SELECT productid, SUM(quantity) quantity
FROM Inventory_Transactions
WHERE transactiontype = 2
GROUP BY productid
ORDER BY productid;
CREATE VIEW onhold AS
SELECT productid, SUM(quantity) quantity
FROM Inventory_Transactions
WHERE transactiontype = 3
GROUP BY productid
ORDER BY productid;
SELECT pur.productid, pur.quantity - so.quantity - on.quantity
FROM purchased pur, sold so, onhold on
WHERE pur.productid = so.productid = on.productid;
But I got this error
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'on WHERE pur.productid = so.productid = on.productid' at line 1
These are the individual VIEW I created above:
SELECT * FROM purchased;
+-----------+----------+
| productid | quantity |
+-----------+----------+
| 1 | 40 |
| 3 | 100 |
| 4 | 40 |
| 5 | 40 |
| 6 | 100 |
| 7 | 40 |
| 8 | 65 |
| 14 | 40 |
| 17 | 40 |
| 19 | 85 |
| 20 | 40 |
| 21 | 20 |
| 34 | 510 |
| 40 | 120 |
| 41 | 290 |
| 43 | 650 |
| 48 | 200 |
| 51 | 40 |
| 52 | 100 |
| 56 | 120 |
| 57 | 180 |
| 65 | 40 |
| 66 | 80 |
| 72 | 90 |
| 74 | 20 |
| 77 | 60 |
| 80 | 75 |
| 81 | 325 |
+-----------+----------+
28 rows in set (0.00 sec)
SELECT * FROM sold;
+-----------+----------+
| productid | quantity |
+-----------+----------+
| 1 | 15 |
| 3 | 50 |
| 4 | 40 |
| 5 | 25 |
| 6 | 100 |
| 7 | 40 |
| 8 | 65 |
| 17 | 40 |
| 19 | 85 |
| 20 | 40 |
| 21 | 20 |
| 34 | 487 |
| 40 | 120 |
| 41 | 290 |
| 43 | 325 |
| 48 | 200 |
| 51 | 40 |
| 52 | 40 |
| 57 | 100 |
| 72 | 90 |
| 74 | 20 |
| 80 | 55 |
| 81 | 200 |
+-----------+----------+
23 rows in set (0.01 sec)
SELECT * FROM onhold;
+-----------+----------+
| productid | quantity |
+-----------+----------+
| 1 | 25 |
| 34 | 23 |
| 43 | 325 |
| 56 | 110 |
| 80 | 20 |
| 81 | 75 |
+-----------+----------+
6 rows in set (0.00 sec)
I am using the northwind database
Here are all the table relationship in northwind database.
May I know how do I solve this?
The approach I would take is using something called "Conditional aggregation"
use a summed case expression to determine if it's a purchase
use a summed 2nd case expression to determine if it's a sale/hold
subtract the two
group by product
.
SELECT ProductID,
SUM(case when TransactionType = 1 then quantity else 0 end) -
SUM(case when TransactionType in (2,3) then quantity else 0 end) as AvailableInventory
FROM Inventory_Transactions
GROUP BY ProductID
As to the nature of your error:
you have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'on WHERE pur.productid = so.productid = on.productid' at line 1
on is a reserved word use onh or onhold or something for the alias
using , joins is and old technique use Inner, outer, full outer, cross join and use the on notation to specify how he tables relate
equality checks within where clauses must return true false. To date they are not trinary. If A=B but <> C what would you expect to happen? so break out your productID's so they handle all the needed joins.
you're going to have problems of matching records because not all 3 may have a value so if you match on productID for sold or hold, you may not have a product and it would fall out of your results... thus outer joins are needed if you continue with your current approach.
.
ORIGINAL
SELECT pur.productid, pur.quantity - so.quantity - on.quantity
FROM purchased pur, sold so, onhold on
WHERE pur.productid = so.productid = on.productid;
should be:
SELECT coalesce(pur.productid, so.productid,onhold.ProductID) as productid,
coalesce(pur.quantity,0) - coalesce(so.quantity,0) -
coalesce(onhold.quantity) as AvailableInventory
FROM purchased pur
FULL OUTER JOIN sold so
on pur.productid = so.productid
FULL OUTER JOIN onhold
on pur.productid = onhold.productid
OR so.productid = onhold.productid
GROUP BY coalesce(pur.productid, so.productid,onhold.ProductID);
Breaking this down
the coalesce of productID is because we don't know in which of the 1,2,3 a productID will exist. But, we need the product id to show up for any of the 3 not just purchases.
the coalesce on quantity is because if we try to subtract a NULL value, we get a NULL so we need to ensure a numeric value exists. pretend for a second product ID 1 exists in purchases but has had no sales or holds. 10 purchases - NULL = NULL... not what we want. now if the productID 2 is in sales but not purchases we'd have NULL-10-NULL... again not what we want. Also if we just used the productid from purchases it would be NULL on this record...; also not good.
we replaced the , notation which is a cross join to a full outer so we don't lose records. Now cross join would work but it takes # of purchases * #of sales * number of on hold then limits the records based on your where clause this is WAY more work than the database needs to do and on a large dataset would be VERY slow.
Discussed earlier, where clause can't be trinary it must be a binary result comparing just 2 values.
Now full outer vs left vs inner vs right....
FULL OUTER: include all records from both tables and line them up where they match on Key (productID in our example)
LEFT JOIN include all records from the first table and any that match from the 2nd
INNER Include only records which exist in both tables.
RIGHT include all records from the 2nd table and any that match from the 1st.
CROSS JOIN: all records related to all records (Very slow but there are uses; just not here)
and more...
If we can't assume each of your products has a value in each group: pur, sold, onhold; we need to use full outer joins as above; so we don't exclude any records. However even this can be an issue because we have to ensure each productID ties back to a related product in another table if it exists.
To do this we have to use an OR on the last match (kinda ugly) and as we don't know the source of productID... we have to find one using coalesce.
If we can assume all products must first exist as a purchase, we can left join the other two. A bit cleaner, but we still have to handle the situation where there may be no data in sold or on hold, so we have to coalesce the 0 in for the quantities. we don't have to coalesce the productid since we know a purchase has to exist.
SELECT pur.productid, sum(coalesce(pur.quantity,0) - coalesce(so.quantity,0) -
coalesce(onhold.quantity)) as AvailableInventory
FROM purchased pur
LEFT OUTER JOIN sold so
on pur.productid = so.productid
LEFT OUTER JOIN onhold
on pur.productid = onhold.productid
--Note: we tie back to pur both times as we know it exists there.
GROUP BY pur.productid;
Hopefully you can see the conditional aggregation is the better approach as it simplifies and reduces joins and avoids much of the coalesing we do.

Output record without duplicate

I have a table like this and i want to output without duplication of the same user. If i use group by it shows only one record on the same column. iam also using left join for location and user name. A little help
+------+---------+----------+---------+
| user | work id | location | time |
+------+---------+----------+---------+
| 1 | 42 | 1 | 2hr |
| 1 | 42 | 1 | 10min |
| 1 | 42 | 1 | 30min |
| 2 | 42 | 1 | 4hr |
| 2 | 42 | 1 | 2.30min |
| 1 | 50 | 2 | 4min |
| 1 | 50 | 2 | 5min |
| 2 | 20 | 3 | 3hr |
| 1 | 20 | 3 | 6hr |
+------+---------+----------+---------+
Iam looking for this
+------+---------+----------+
| user | work id | location |
+------+---------+----------+
| 1 | 42 | 1 |
| 1 | 50 | 2 |
| 1 | 20 | 3 |
| 2 | 42 | 1 |
| 2 | 20 | 3 |
+------+---------+----------+
You simply need a distinct clause here -
SELECT DISTINCT user
,workid
,location
FROM YOUR_TABLE
ORDER BY user
,location

MySQL - Subtract previous day data from current day data, with special conditions

I have the following example data:
ID | DATE | LOCATION | DATA
----|-------|----------|------
1 | MAR-1 | 1 | 100
2 | MAR-2 | 1 | 120
3 | MAR-3 | 1 | 160
4 | MAR-3 | 2 | 80
5 | MAR-4 | 1 | 170
6 | MAR-4 | 2 | 100
7 | MAR-5 | 1 | 10
8 | MAR-6 | 1 | 50
I need to subtract the previous day's data from the current day's and put the result into a "delta" column, where the result is also unique per location. In this example, the data is from a power meter, which is reset to zero periodically. So, I need to test if the "delta" result is negative, and if so, copy only the raw "data" value in the "delta" column. Based on the above example data, I would like this output:
ID | DATE | LOCATION | DATA | DELTA | COMMENT
----|-------|----------|------|-------|-------------------------------
1 | MAR-1 | 1 | 100 | NULL | NO PREVIOUS DATA EXISTS
2 | MAR-2 | 1 | 120 | 20 |
3 | MAR-3 | 1 | 160 | 40 |
4 | MAR-3 | 2 | 80 | NULL | NO PREVIOUS DATA EXISTS
5 | MAR-4 | 1 | 170 | 10 |
6 | MAR-4 | 2 | 100 | 20 |
7 | MAR-5 | 1 | 10 | 10 | DELTA WOULD BE - SO TAKE DATA
8 | MAR-6 | 1 | 50 | 40 |
I'm using MySQL v5.7.
You can use subquery to find the output like following.
SELECT id,date,location,data,
CASE
WHEN nv < 0 THEN data
ELSE nv
END AS Delta
FROM (SELECT *,
data - (SELECT data
FROM mytable t1
WHERE t1.id < t2.id
AND t1.location = t2.location
ORDER BY id DESC
limit 1)nv
FROM mytable t2) t
Online Demo
Output
| id | date | location | data | Delta |
| --- | ----- | -------- | ---- | ----- |
| 1 | MAR-1 | 1 | 100 | |
| 2 | MAR-2 | 1 | 120 | 20 |
| 3 | MAR-3 | 1 | 160 | 40 |
| 4 | MAR-3 | 2 | 80 | |
| 5 | MAR-4 | 1 | 170 | 10 |
| 6 | MAR-4 | 2 | 100 | 20 |
| 7 | MAR-5 | 1 | 10 | 10 |
| 8 | MAR-6 | 1 | 50 | 40 |

Picking out specific values from a group in MySQL

This seems like such a simple problem, but I can't find a good solution. I'm trying to select information from a slightly misformatted table. Basically, wherever sequence=0, the person_id should actually be a company_id. This company_id then applies to all the rows which have the same group_id.
Someone thought it was a good idea to format things this way instead of simply having a company_id column, but it makes trying to select by company very difficult. It would make my programming much easier to simply add this extra column, and fix the formatting.
I want to turn something like this:
+----------+------------+-----------+----------+
| group_id | date | person_id | sequence |
+----------+------------+-----------+----------+
| 1 | 2012-08-31 | 10 | 0 |
| 1 | 2012-08-31 | 11 | 1 |
| 1 | 2012-08-31 | 12 | 2 |
| 2 | 1999-04-16 | 10 | 0 |
| 2 | 1999-04-16 | 21 | 1 |
| 2 | 1999-04-16 | 22 | 2 |
| 2 | 1999-04-16 | 23 | 3 |
| 2 | 1999-04-16 | 24 | 4 |
| 3 | 2001-01-09 | 30 | 0 |
| 3 | 2001-01-09 | 31 | 1 |
| 3 | 2001-01-09 | 11 | 2 |
| 3 | 2001-01-09 | 12 | 3 |
+----------+------------+-----------+----------+
Into this:
+------------+----------+------------+-----------+----------+
| company_id | group_id | date | person_id | sequence |
+------------+----------+------------+-----------+----------+
| 10 | 1 | 2012-08-31 | 11 | 1 |
| 10 | 1 | 2012-08-31 | 12 | 2 |
| 10 | 2 | 1999-04-16 | 21 | 1 |
| 10 | 2 | 1999-04-16 | 22 | 2 |
| 10 | 2 | 1999-04-16 | 23 | 3 |
| 10 | 2 | 1999-04-16 | 24 | 4 |
| 30 | 3 | 2001-01-09 | 31 | 1 |
| 30 | 3 | 2001-01-09 | 11 | 2 |
| 30 | 3 | 2001-01-09 | 12 | 3 |
+------------+----------+------------+-----------+----------+
The only way I can think of how to achieve this is with nested SELECT statements, which are very inefficient considering I have about 100M rows. It's a one time fix though, so I don't mind letting it run overnight.
If you permanently want to change your table to include a company_id column then do this:
First alter the table and add the new column:
alter table your_table add company_id int;
Then update all rows to set the company to the person_id = 0 for the group:
UPDATE your_table a
JOIN your_table b ON a.group_id = b.group_id
SET a.company_id = b.person_id
WHERE b.sequence = 0;
And finally remove the rows with sequence = 0:
DELETE FROM your_table WHERE sequence = 0;
Sample SQL Fiddle
The end result will be:
| group_id | date | person_id | sequence | company_id |
|----------|------------|-----------|----------|------------|
| 1 | 2012-08-31 | 11 | 1 | 10 |
| 1 | 2012-08-31 | 12 | 2 | 10 |
| 2 | 1999-04-16 | 21 | 1 | 10 |
| 2 | 1999-04-16 | 22 | 2 | 10 |
| 2 | 1999-04-16 | 23 | 3 | 10 |
| 2 | 1999-04-16 | 24 | 4 | 10 |
| 3 | 2001-01-09 | 31 | 1 | 30 |
| 3 | 2001-01-09 | 11 | 2 | 30 |
| 3 | 2001-01-09 | 12 | 3 | 30 |

MySQL query works in Workbench, but not within code

Using MySQL 5.5.30, for a table, purchase_order_product (MyISAM), that has the following records:
+----------+------------------+
| order_id | order_product_id |
+----------+------------------+
| 98 | 1 |
| 99 | 14 |
| 99 | 15 |
| 100 | 16 |
| 100 | 17 |
| 100 | 18 |
| 101 | 19 |
| 102 | 20 |
+----------+------------------+
When I run the following query from MySQL Workbench 5.2.47 CE:
SELECT pop.order_id, pop.order_product_id,
#RUNNING:=IF(#PREVIOUS = pop.order_id, #RUNNING, 0) + 1 AS rownum,
#PREVIOUS:=pop.order_id as previd
FROM purchase_order_product pop
ORDER BY pop.order_id , pop.order_product_id ASC;
I get the following output:
+----------+------------------+--------+--------+
| order_id | order_product_id | rownum | previd |
+----------+------------------+--------+--------+
| 98 | 1 | 1 | 98 |
| 99 | 14 | 1 | 99 |
| 99 | 15 | 2 | 99 |
| 100 | 16 | 1 | 100 |
| 100 | 17 | 2 | 100 |
| 100 | 18 | 3 | 100 |
| 101 | 19 | 1 | 101 |
| 102 | 20 | 1 | 102 |
+----------+------------------+--------+--------+
My desired goal is the value of rownum, which is fine and as expected so far...
HOWEVER, when I run the query from within my PHP code OR from the mysql command line against the same database using the same db user, I get the following output:
+----------+------------------+--------+--------+
| order_id | order_product_id | rownum | previd |
+----------+------------------+--------+--------+
| 98 | 1 | 1 | 98 |
| 99 | 14 | 1 | 99 |
| 99 | 15 | 1 | 99 |
| 100 | 16 | 1 | 100 |
| 100 | 17 | 1 | 100 |
| 100 | 18 | 1 | 100 |
| 101 | 19 | 1 | 101 |
| 102 | 20 | 1 | 102 |
+----------+------------------+--------+--------+
As you can see, rownum is always 1!
I have search extensively for a solution to this perplexing issue, to no avail. Does anyone have any idea what may be going on? Am I doing something wrong?
You need to initialize the variable:
SET #RUNNING:=0;
SET #PREVIOUS:=0;
This must be done before your query and in the same mysql session.