My table columns are invoiceid (primary AI), invoiceno, purchaseorderno, userid. I figured I couldn't use invoiceid since for example: when a customer buys 2 products, the 2 invoiceid will be diff. So I decided to use invoiceno that whenever the user will click order and he bought for example 2 products, they will have the same invoiceno. I have this code below:
$invoice = 0000000;
$invoiceno = $invoice + 1;
$sqlString1 = "INSERT INTO invoice
(invoiceno, purchaseorderno, userid)
VALUES ('$invoiceno', '$purchaseorderno', '$userid');";
$result1 = mysqli_query($conn, $sqlString1);
It works but then the invoice no stay as 1. It doesn't increment when I click order. If my idea is wrong, suggestions would be greatly appreciated.
I would do it in two tables not one.
You have invoice and invoice_item Which have a One to Many relationship.
If you make another table for the line items, all you problems evaporate.
Table invoice
id
date
total
status etc...
Table invoice_item
id
invoice_id - foreign key
product_id
quantity
etc...
Selecting them is no problem
SELECT
{fields}
FROM
invoice AS i
JOIN
invoice_item AS l ON i.id = l.invoice_id
WHERE
i.id = :id
And so on....
Then you can just use the Primary key from invoices and your all set. It will also be more robust (less prone to breaking) probably faster, probably easier to code in the long run and so on.
It does add a tiny bit of complexity to the data model, and a bit more caution when doing inserts and deletes and what not, but if you setup the foreign key restraints correctly, use transactions when doing inserts, you should be fine.
UPDATE
Here is a DBfiddle (click it) with a number of examples of how to setup the forign key and how to build the basic table relationship. I just guessed at these fields you can of course make any changes you need to, to add or remove them. This is just for the sake of explaining it.
CREATE TABLE invoice(
id INT(10) unsigned NOT NULL AUTO_INCREMENT,
total DECIMAL(6,2) unsigned NOT NULL,
email VARCHAR(100),
submission_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY ( id )
) ENGINE=InnoDB;
CREATE TABLE invoice_item(
id INT(10) unsigned NOT NULL AUTO_INCREMENT,
invoice_id INT(10) unsigned NOT NULL,
product_id INT(10) unsigned NOT NULL,
qty INT(3) unsigned NOT NULL,
sub_total DECIMAL(6,2) unsigned NOT NULL,
PRIMARY KEY ( id ),
CONSTRAINT FK_InvoiceItems FOREIGN KEY(invoice_id) REFERENCES invoice(id) ON DELETE CASCADE ON UPDATE CASCADE,
KEY(product_id)
) ENGINE=InnoDB;
Just a few Query examples:
Insert invoice
#in the example fiddle this is AI #2 so invoice.id=2
INSERT INTO invoice (total,email) VALUES ('100.00','user2#example.com');
Insert related invoice items
#both of these line items have same invoice,
INSERT INTO invoice_item (invoice_id,product_id,qty,sub_total) VALUES ('2','200','7','70.00');
INSERT INTO invoice_item (invoice_id,product_id,qty,sub_total) VALUES ('2','270','2','30.00');
Basic Inner Join (selects all invoices that have items, and their items)
SELECT
i.*, l.id AS invoice_item_id, l.product_id, l.qty
FROM
invoice AS i
LEFT JOIN
invoice_item AS l ON i.id = l.invoice_id;
Count the number of unique Items
SELECT
invoice_id, COUNT(id) AS unique_items
FROM
invoice_item
GROUP BY invoice_id;
Count the number of total items. You can sum, sub_total to get total the same way. You could remove invoice.total if you want to. It can be nice to have the total counted up in there, but you also have to keep it updated when/if you change the number of items in an invoice.
SELECT
invoice_id, SUM(qty) AS total_items
FROM
invoice_item
GROUP BY invoice_id;
Find any invoices without items (its impossible to add items without invoices with the fk constraint, which is exactly what we want)
SELECT
i.*, l.id AS invoice_item_id, l.product_id, l.qty
FROM
invoice AS i
LEFT JOIN
invoice_item AS l ON i.id = l.invoice_id
WHERE
l.id IS NULL;
And Last, Delete an invoice, because of the constraint this will cascade and delete the related items from invoice_item with no extra work,in other words the items are auto-magicaly deleted. I've also found letting the database do the cascade delete is significantly faster then manually deleting them.
DELETE FROM invoice WHERE id=2;
In this data model, I have the invoice_items grouped by products. So for invoice.id #2, we have two items, one with product id #200, which is toy ufo's. The other is #270 which is metallic weather balloons. Tiny Tim got 7 UFO's for $10 each for a sub_total of $70, and then he got 2 weather balloons for $15 each (nothing our government does is cheap).
Ok that is my DB 101 lesson for the day.
UPDATE1
Based on this error
Cannot add or update a child row: a foreign key constraint fails
This is actually a good thing, it's the purpose of the foreign key. I will try to explain
If you do this insert
INSERT INTO invoice (total,email) VALUES ('100.00','user2#example.com');
This creates a row in invoice, it's the purpose of insert. Now because it's Auto Increment Id and this is the first row it's id is 1. Because it's the first row its also the only record in invoice. We can all agree on that, I insert 1 row in a new table, that makes it the first and only row. And it has an AI id of 1
Now if I try to insert
INSERT INTO invoice_item (invoice_id,product_id,qty,sub_total) VALUES ('2','200','7','70.00');
This row has an invoice_id of 2, and we just said we have one invoice with an id of 1. So there is no invoice with an id of 2. This will give you the same error you just got, because you are trying to add an invoice_item without having it's associated invoice, which is something we never want to happen. This is called an "orphan" row because it's parent row does not exist. If we allowed this our Database could be filled with invoice items that have no invoice. Then if we later add an invoice with the id of one of those orphan rows we have just associated things that should not be associated. This is why that error makes sense and why it's a good thing.
The obvious way to fix this is to not insert invoice_items that don't belong to an invoice.
I really hope that makes sense.
So how do we insert invoice_items that do have an invoice?
In PHP you want to insert the invoice then get it's id, then insert the invoice_item. See this page.
http://php.net/manual/en/mysqli.insert-id.php
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
/* check connection */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
//insert invoice
$query = "INSERT INTO invoice (total,email) VALUES ('100.00','user2#example.com')";
$mysqli->query($query);
//insert invoice item with last insert id
$query = "INSERT INTO invoice_item (invoice_id,product_id,qty,sub_total) VALUES ('".$mysqli->insert_id."','200','7','70.00')";
The important part here is to use, $mysqli->insert_id when inserting invoice items. Now if you want to add an invoice item to an existing invoice that was inserted some time ago. Then you just query the invoice, and pull it's id out and use that for the new invoice item.
Hope that helps, enjoy!
Related
In my database I have one table that contains a complete list of products, and another table that contains the same list of products on the x-axis, with a list of customers on the y-axis, where the value for each product can be 1 or 0 depending on whether that customer can view that product. My SQL looks like this:
SELECT products.product_code, products.product_type, products.product_category, products.product_title, products.product_description
FROM product_lists
INNER JOIN products
ON product_lists.product_code=products.product_code
WHERE product_lists.customer="1"
ORDER BY products.product_code
My problem is that I would like to create a view of this result for each customer to use as that customers product table, however when I create it I get the message "This table does not contain a unique column. Grid edit, checkbox, Edit, Copy and Delete features are not available." even though the product_code field is set as a primary key in both the products table and the product_lists table.
How can I create a join/view that uses the primary key from the table(s) it was created from? In short I would like the product_code field to become the primary key of my view.
Thanks!
I think the problem is the join. You can fix this by moving the condition to the where clause. MySQL doesn't allow subqueries in the from, but it does in the where:
SELECT p.product_code, p.product_type, p.product_category, p.product_title, p.product_description
FROM products p
WHERE EXISTS (SELECT 1
FROM product_lists pl
WHERE pl.product_code = p.product_code AND
pl.customer = 1
)
ORDER BY p.product_code;
Im building a shop and each product has a unique set of attributes.
the product db looks like this:
products_id, relation_id, product_name, description,
price, glass, shipping, img, cat, subcat, model
Since every product has several (~40) different attributes unique to that product only ive created a second table to store them.
products_id, att_name, att_val, att_head, att_standard, att_order
This works fine, because there will never be two unique rows.
The problem, however, is when i need to modify the attributes content.
using MySQL Workbench i can modify a row using something like
UPDATE product_attributes SET att_val='1500' WHERE products_id='112' AND att_head='threshold'
This however, doesn't seem to work when i update from my php script.
Is there an easy way to modify the table to support updating?
Im well aware of the stupidity not having an unique column.
But im not sure how to make the two tables relate.
Where should i store the unique id of the attributes?
One choice,
add a primary key "auto_incremented" into the product_attributes table...
ALTER TABLE `product_attributes` ADD `id` INT( 10 ) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST
This Id is just for CRUD (Create Read Update Delete) task.
The only relation you can have between your two tables is the products_id wich allow you to have few product_attributes for one product
Since 1 product has more than 1 unique attributes that u store in a second table, you should use the ID of the table product and store it in the second table with the attributes.
Hope this is what u need?
I'm trying to produce a page that would function a bit like a digital sticker album. My SQL knowledge isn't very good so I need a bit of help finishing off what I have so far.
I want to display a list of all available stickers but then also show whether or not a user has a sticker. So the idea is to display a list of empty boxes(items) and then display an image inside the box if the user owns the sticker.
The two relevant tables I have are called "items" and "inventory". Items contains all the available stickers and inventory contains the stickers owned by the users.
Here are the available columns:
CREATE TABLE items (
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255)
) Engine=InnoDB;
CREATE TABLE inventory (
userid INT UNSIGNED,
itemid INT UNSIGNED,
-- FOREIGN KEY (userid) REFERENCES users (id),
FOREIGN KEY (itemid) REFERENCES items (id),
UNIQUE (itemid, userid)
) Engine=InnoDB;
I'm open to suggestions on the best way to go about doing this using PHP and MySQL, but I think what I need is a query to return a list of all the item names and then another column to flag whether the user has the item. I can then loop through the items in php and then use a conditional based on the second column to show if the sticker is there or not.
So far i've got as far as the below query but it needs to only show items for the current user. Sticking in a 'where' clause doesn't work either as it then only shows the inventory items and not the NULL items (that is, it doesn't include all items).
SELECT items.name, inventory.userid
FROM items
LEFT JOIN inventory ON items.id = inventory.itemid
SELECT items.name, inventory.userid
FROM items
LEFT JOIN inventory ON items.id = inventory.itemid
WHERE inventory.userid = '$userid'
Try moving the userid test to the JOIN condition:
SELECT items.name, inventory.userid
FROM items
LEFT JOIN inventory
ON items.id = inventory.itemid AND inventory.userid=?
That way, when the join condition fails (such as when the item is in another user's inventory), the item itself is still included in the result, but with a null userid. It can also make use of an appropriately defined index on table inventory.
Correct me if I've misunderstood, and I will update accordingly.
You are outputting a table of all your items.
Each item listed
will have an empty "box", that is filled if the current user has this
item.
To do this, you should only need 2 queries-- one to fetch all your items, another to fetch the item IDs of the current user's inventory.
// Get all current user's items
$qUsersItems = "SELECT items.id AS itemID FROM items, inventory WHERE items.id = inventory.itemid AND inventory.userid = " . $givenUserID .
$rUsersItems = mysql_query($qUserItems);
// Store the user's items' IDs into an array
while (list($itemID) = mysql_fetch_assoc($rUsersItems))
$usersItems[] = $itemID;
// Get all items
$qAllItems = "SELECT id, name FROM items";
$rAllItems = mysql_query($qAllitems);
// ... (To wherever you are outputting your items list)
while ($item = mysql_fetch_assoc($rAllItems)) {
if (in_array($item['id'], $usersItems)) // Since this ID is in the users list of items (ids)
$fillBox = "HTML to fill box";
else
$fillBox = "";
/* Your "per item" HTML here, for example (assuming you have outputted table tags before and after the while loop */
echo<<<HTML
<tr>
<td>{$item['name']</td>
<td>{$fillBox}</td> <!-- This would be your empty box /-->
</tr>
HTML;
Note: This code is untested, and unfinished for obvious reasons. After I wrote this, I realize why people ask to show what you have done first.
I have an ajax live table edit to change the price of my current product. What I want to do is insert the price before I change it and then insert the updated price. The reason being is because I want to show the change in the updated price. Example: current price is $54.00 and I change it to $57.00. I need to keep a log of price change throughout the day and show the price change of $3.00. How would I go about inserting the old price while the updated price gets inserted also. Thanks.
I suggest you make your price table like this
table price
-----------
id unsigned integer autoincrement primary key
article_id integer /*link to articletable*/
valid_from date
valid_until date
amount decimal(10,2) /*always use decimal for money*/
Then you can insert your new price using the following 4 queries.
/*hide changes from the rest of the world until we are done*/
START TRANSACTION
/*invalidate the latest existing price in the price table*/
UPDATE price
SET valid_until = DATESUB(CURDATE(),INTERVAL 1 DAY)
WHERE article_id = '100' ORDER BY valid_until DESC LIMIT 1
/*the order by selects the latest item, the limit does only 1 update*/
/*insert the new price*/
INSERT INTO PRICE (article_id, valid_from, valid_until, amount)
VALUES ('100', CURDATE(), DATEADD(CURDATE(),INTERVAL 100 YEAR), '99.95')
/*show changes to the rest of the world*/
COMMIT
You need the transaction or you risk the price table being out of sync. Set the tabletype to InnoDB on the price table.
Al your other tables can be MyISAM, just make sure the price table is InnoDB.
You can now select prices by using:
SELECT article.name
,price.amount as price_per_item
,purchase.qty as number_of_items
,price.amount * purchase.qty as amount
FROM purchase
INNER JOIN article ON (article.id = purchase.article_id)
INNER JOIN price ON (price.article_id = purchase.article_id)
AND (purchase.transactiondate BETWEEN price.valid_from and price.valid_until)
WHERE purchase.id = '458'
you can maintain to different fields for the two. Like old_value and new_value. At the end of the day you can tally the values and print the difference.
I'm not even sure if this is possible. I am using Kohana framework(ver 2.3). I have 2 separate databases. One called 'employees' and another called 'tracker'. The databases are used in 2 different websites. I want to eliminate a table in the tracker database called 'csr', which contains identical employee data, and link the tracker to the employee info in the employees database.
In my tracker application I have a model setup for employees which references the external 'employees' database. I can query it with ORM from the tracker application and all is well. the unique key for employees is 'id'.
In my tracker database I have a model for 'records' table with about 12k entries. None of the field names correspond to any field names in the employees table from the employees database but some fields do contain identical information. The unique key for 'records' is Transaction_Number
Please note I did not write this application or design the databases. I am trying to "retro-fit" the tracker application to use the, now centralized, employee data .
There are 9 fields in 'records' that contain matching information in the employees database. This 9 fields contain employee id's and names but are not all the same id.
I can change the data in these 9 fields so that they are all employee id's if it would help but I need to be able to get employee data: names, addresses, etc., based on the id in any of those 9 fields
Redesigning the database would cause a rewrite of the tracker application and I really don't have the time to do all that.
To save some reading, I am not including the table structures here but I can add them if needed.
What can I do to link these two tables together?
EDIT: Added table structure for tracker.records
TRACKER.RECORDS
Transaction_Number (PK AI not null)
date
accountnumber
reasoncode
reasondesc
actioncode
actiondesc
comments
supervisor - employee->id (supervisor that created the record)
supername - employee->name
supersuper - employee->parent->name
superman - employee->parent->parent->name
eid - employee->id (employee that the record is about)
Service_Rep - employee->name
ServRepSupervisor - employee->parent->name
ServRepManager - employee->parent->parent->name
csrfollow - employee->name (who to follow up with)
Important
Read
Followup_Read
followup_Important
the employee table is using ORM_Tree to be self relational.
I need to be able to get employee info for any of those fields. I can change the data in each of those fields to be an employee id and i think i can eliminate some of them. the only ones I rally need are supervisor(employee->id), eid(employee->id) and csrfollow(can be changed to employee->id). the other fields can be discovered based on the employee->id. I still need to have those 3 fields point to the employee.id field in the employees database.
Are you aware that MySQL allows foreign keys to reference tables across databases, as long as both databases are hosted on the same instance of MySQL?
CREATE TABLE `employees` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY
-- other columns...
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `records` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
`employee_id` bigint(20) unsigned DEFAULT NULL,
-- other columns...
FOREIGN KEY (`employee_id`) REFERENCES `employees`.`employees` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
You just have to qualify the referenced table name with its database name.
Re your update: You can change the name of RECORDS to RECORDS_BASE with the distinct data that belongs in TRACKER.
TRACKER.RECORDS_BASE
Transaction_Number (PK AI not null)
date
accountnumber
reasoncode
reasondesc
actioncode
actiondesc
comments
supervisor_id
eid
Important
Read
Followup_Read
followup_Important
Then create a new VIEW called RECORDS that joins RECORDS_BASE to multiple rows in EMPLOYEES:
CREATE VIEW TRACKER.RECORDS AS
SELECT rb.*,
s.id AS supervisor,
s.name AS supername,
ss.name AS supersuper,
sss.name AS superman,
emp.name AS Service_Rep,
srs.name AS ServRepSupervisor,
srm.name AS ServRepManager,
??? AS csrfollow
FROM TRACKER.RECORDS_BASE AS rb
JOIN EMPLOYEES.EMPLOYEES AS s ON rb.supervisor_id = s.id
JOIN EMPLOYEES.EMPLOYEES AS ss ON s.parent_id = ss.id
JOIN EMPLOYEES.EMPLOYEES AS sss ON ss.parent_id = sss.id
JOIN EMPLOYEES.EMPLOYEES AS emp ON rb.eid = emp.id
JOIN EMPLOYEES.EMPLOYEES AS srs ON emp.parent_id = srs.id
JOIN EMPLOYEES.EMPLOYEES AS srm ON srs.parent_id = srm.id;
I can't tell from your description what belongs in the csrfollow column. Whose name is it? Anyway I'll leave that for you to decide. I've shown how you can get a reference to each of the relevant rows in the employees table, so take your pick.