Check values of two tables? - database

I'm building an application for a University assignment, I'm using Visual Basic 2010 and Micrsoft Access for the database.
I have a form where customers can order products each product will have a set price, for example product1 is £1, however some customers will get products for cheaper so I have a table 'special prices' the special price table will contain rows including the 'customerid' 'productid' and the 'specialprice'.
I need to know how to display the price, if a customer chooses product1 I need my application to first check if they have a special price and if not just display the normal price,
Thanks,
Jack.

If Customer does not have a special price, it is null
SELECT p.Price, c.CustomerID, sp.SpecialPrice, IFNULL(sp.SpecialPrice, p.Price) As UsedPrice
FROM Product p
LEFT JOIN SpecialPrice sp ON p.ProductID = sp.ProductID
AND sp.CustomerID = 123
My answer is based on SQL Server I hope you can translate it to MSAccess. The main point is Join the Product table to special price table where current customer. If special price is available use it, otherwise use price in Product
OR in code
if(row["SpecialPrice"] != null) // if(row["SpecialPrice"] != DBNull.Value) --- instead
price = row["SpecialPrice"];
else
price = row["Price"];

Related

For each maker who has models at least in one of the tables PC, Laptop, or Printer, determine the maximum price for his products. Output: maker;

TASK: for each maker who has models at least in one of the tables PC, Laptop, or Printer, determine the maximum price for his products. Output: maker; if there are NULL values among the prices for the products of a given maker, display NULL for this maker, otherwise, the maximum price.
This is task 41 from this website: http://www.sql-ex.ru/
Database description:
The database scheme consists of four tables:
Product (maker, model, type)
PC (code, model, speed, ram, hd, cd, price)
Laptop (code, model, speed, ram, hd, screen, price)
Printer (code, model, color, type, price)
The Product table contains data on the maker, model number, and type of product ('PC', 'Laptop', or 'Printer'). It is assumed that model numbers in the Product table are unique for all makers and product types. Each personal computer in the PC table is unambiguously identified by a unique code, and is additionally characterized by its model (foreign key referring to the Product table), processor speed (in MHz) – speed field, RAM capacity (in Mb) - ram, hard disk drive capacity (in Gb) – hd, CD-ROM speed (e.g, '4x') - cd, and its price.
The Laptop table is similar to the PC table, except that instead of the CD-ROM speed, it contains the screen size (in inches) – screen. For each printer model in the Printer table, its output type (‘y’ for color and ‘n’ for monochrome) – color field, printing technology ('Laser', 'Jet', or 'Matrix') – type, and price are specified.
I've tried several queries, most of them worked on the first database but not on the second one
Here's my last tried query:
SELECT DISTINCT
maker,
CASE
WHEN price IS NULL
THEN NULL
ELSE MAX(price) OVER (PARTITION by maker)
END max_p
FROM
(SELECT DISTINCT maker, price
FROM product
LEFT JOIN pc ON pc.model = product.model
WHERE product.model IN (SELECT model FROM pc)
UNION
SELECT DISTINCT maker, price
FROM product
LEFT JOIN laptop ON laptop.model = product.model
WHERE product.model IN (SELECT model FROM laptop)
UNION
SELECT DISTINCT maker, price FROM product
LEFT JOIN printer ON printer.model = product.model
WHERE product.model IN (SELECT model FROM printer)) as x
Your query returned the correct dataset on the first (available) database, but it returned incorrect dataset on the second checking database.
Wrong number of records (more by 3)
Most expensive product per maker, or null if any of the maker's prices are null:
SELECT
pro.maker,
NULLIF(MAX(COALESCE(pri.price, 922337203685477)), 922337203685477) as price
FROM
product pro
INNER JOIN
(
SELECT model, price FROM printer
UNION ALL
SELECT model, price FROM pc
UNION ALL
SELECT model, price FROM laptop
) pri
ON pri.model = pro.model
GROUP BY pro.maker
How it works:
Strip the laptop, pc and printer tables out to just model and price (including any null prices) and union then together to provide a consistent data set. Join this to product to get the maker. Group by the maker looking for the max price. Because we have to show null as the max price of there is any null price we COALESCE to make all the null prices a massively high price so they will surely be the max price, then we turn that massively high price back into null with NULLIF
NULLIF is proprietary. I could do the same thing with case when max(...) = 922... then null else max(...) end But it's more wordy
Trivia:
922337203685477 Is almost the max value for a MONEY; I omitted the fractional component
Also consider:
Two of the three hardware tables have a type column. It is not stated whether this relates to the type column of the product table. If it is you'll have to add it into the join. For the table that lacks a type, null it and make the join condition include "or pri.type is null"

Cascading Hierarchical Join Conditions

I'm currently putting together a schema that will be responsible for storing products, prices and margins.
The crux of the problem I'm having is how best to handle multiple scenarios.
Definitions - All these are fields in the Link (Intersection) table
Product - A widget
Margin - An data structure that represents how to alter the purchase
price to determine retail price. (complex enough to require a
separate table)
Supplier - Someone who supplies us with a Product
Authority - Someone the supplier is beholden to
Client - Someone we will retail to
ClientGroup - A collection of Clients
Some of these are optional. There will always be a Product-Margin mapping.
The other fields exist to define more specific relationships.
The rules will be applied with a hierarchy.
Examples:
Product "Foo" has a Margin of 10% (applies to all clients)
For ClientGroup "Group A" Foo has a Margin of 8%
For Client "Bob's Burgers" who is a member of "Group A" Foo has a margin of 6%
That would be covered by 3 rows, with the following fields populated (un-populated fields are null)
Product-Margin
ClientGroup-Margin
Client-Margin
Rule 3 is the most specific, and so would take precedence.
Is this link table to best way to store these hierarchical relationships?
If not, what is?
What is the best way of structuring a query to take advantage of this? I've written a query using temp tables and conditional logic but I cant help but think I'm square-pegging SQL and there's a better way of structuring the query.
I'd like to keep as much of the logic in SQL and out of the business logic.
In other words, the app can call a stored procedure, passing in the Product, and Client plus optionally Authority and /or Supplier and receive the appropriate Margin.
I think in your examples 2 and 3 product should also be populated, otherwise that margin is applied to all products for the client or client group.
The query to get results could be something like this:
SELECT TOP 1 Margin
FROM <table>
WHERE Product = #Product
AND COALESCE(Client,'') = COALESCE(#Client,Client,'')
AND COALESCE(ClientGroup,'') = COALESCE(#ClientGroup,ClientGroup,'')
ORDER BY Client DESC, ClientGroup DESC
# - parameters passed to stored procedure. I don't know if your solution will require joins instead but you could change the where conditions to joins.
This assumes product is always passed as parameter, others are optional (you can add Supplier and Authority there).
Order by desc client means rows that are not null appear on top, if client column is null for all rows then theres the same logic for client group.
Or you can use the order by method suggested in the comment by James B
Thanks for the input folks, the solution for the hierarchical behaviour that I wanted looks something like this:
SELECT TOP 1
FROM MarginLink
WHERE
(
(-- Client selection
(ClientId = #clientId)
OR
ClientGroupId = (SELECT ClientGroupId FROM ClientGroupClient WHERE ClientId = #clientId)
)
AND
(--Product Selection
(#productID BETWEEN ProductIdFrom AND ProductIdTo)
OR
(ProductTypeId = #productTypeID)
OR
(ProductIdFrom IS NULL AND ProductIdTo IS NULL )
)
AND
(-- Supplier
(SupplierId = #supplierId)
OR
(MarginLink.SupplierId IS NULL)
)
AND
(-- Authority
(AuthorityId = #authorityId)
OR
(MarginLink.AuthorityId IS NULL)
)
)
ORDER BY ClientId DESC, ClientGroupId DESC, ProductIdFrom DESC, ProductIdTo DESC, ProductTypeId DESC, SupplierId DESC, AuthorityId DESC

CakePHP - How to use calculations from associated data in query

I'm trying to figure out the best way to do something - basically I'm looking for advice before I do it the long/hard way!
I have the following model associations:
Seller hasMany Invoices
Invoice hasOne Supplier
Supplier belongsTo SupplierType
Each invoice is for a certain amount and is from a certain date. I want to be able to retrieve Sellers who have spent within a certain amount in the past 'full' month for which we have data. So, I need to get the date 1 month before the most recent invoice, find the total on all invoices for that Seller since that date, and then retrieve only those where the total lies between, say, £10000 and £27000 (or whatever range the user has set).
Secondly, I want to be able to do the same thing, but with the SupplierType included. So, the user may say that they want Sellers who have spent between £1000 & £5000 from Equipment Suppliers, and between £1000 & £7000 from Meat Suppliers.
My plan here is to do an inital search for the appropriate supplier type id, and then I can filter the invoices based on whether each one is from a supplier of an appropriate type.
I'm mainly not sure whether there is a way to work out the monthly total and then filter on it in one step. Or am I going to have to do it in several steps? I looked at Virtual Fields, but I don't think they do what I need - they seem to be mainly used to combine fields from the same record - is that correct?
(Posted on behalf of the question author).
I'm posting the eventual solution here in case it helps anyone else:
SELECT seller_id FROM
(SELECT i.seller_id, SUM(price_paid) AS totalamount FROM invoices i
JOIN
(SELECT seller_id, MAX(invoice_date) AS maxdate FROM invoices) sm
ON i.seller_id = sm.seller_id
WHERE i.invoice_date > (sm.maxdate - 30) GROUP BY seller_id) t
WHERE t.totalamount BETWEEN 0 AND 1000
This can be done in a single query that will look something like:
select * from (
select seller, sum(amount) as totalamount
from invoices i join
(select seller, max(invoicedate) as maxdate from invoices group by seller) sm
on i.seller=sm.seller
and i.invoicedate>(sm.maxdate-30)
group by seller
) t where t.totalamount between 1000 and 50000

SQL - Stored Procedures for reporting using Reporting Service

I'm using Reporting Service in a ASP.NET MVC4 project (using Visual Studio 2010) to generate reports and I'm working with stored procedures. I have a table Product which can belong to persons. That's the concept of allocation. A product can be allocated to different persons (many-to-many relation) and I have an intermediate table between those two :
The thing I want tot do in my report is to display the information related to each existing product including the owner. In this situation, I can have 2 types of owners : the company (so the product has never been allocated or the last allocation has an EndDate) or a person (so the last allocation doesn't have an EndDate or has a "future" EndDate).
I know that all these verifications should be made in my report template but for the SQL stored procedure, I have no idea about how to do to get the last allocation for each product (if there is any).
Any idea which could help is welcomed.
EDIT : Updated query
SELECT pr.SerialNumber, coalesce (p.LastName, 'US') as Owner
FROM bm_Products pr
LEFT OUTER JOIN
(Select Id_Person, Id_Product, max(ISNULL(EndDate,getdate()+1)) as MaxAllocDate
FROM bm_ProductAllocations a
WHERE EndDate > getdate()
group by Id_Person, Id_Product
having max(ISNULL(EndDate, getdate()+1)) =
(select max(isnull(EndDate, getdate()+1))
from bm_ProductAllocations where Id_Product = a.Id_Product)
) pa on pr.Id_Product = pa.Id_Product
LEFT OUTER JOIN bm_Persons p on pa.Id_Person = p.Id_Person
The result :
SerialNumber Owner
-------------------------------------------------- --------------------------------------------------
78745148154815204 US
84512048150410522 US
84512841520415205 US
87451284512485120 US
56123051215215215 US
48512485487487856 US
CNU1510ZL0 US
8456656551521 US
4154854854151 US
4851205230047 US
4511120521050 US
84515151320541201 US
74161230326524165 US
I don't see "company" for owner on the table shown, so i'll assume for now that it is in the products table, a hardcoded 'US' because you own it, or you can provide an update.
SELECT pr.Product, coalesce (p.person, 'US') as Owner
FROM Product pr
LEFT OUTER JOIN
(Select id_person, id_product, max(ISNULL(EndDate,getdate()+1)) as MaxAllocDate
FROM Product_Allocation a
WHERE Enddate > getdate()
group by id_person, id_product
having max(ISNULL(EndDate, getdate()+1)) =
(select max(isnull(Enddate, getdate()+1))
from product_allocation where id_product = a.id_Product)
) pa on pr.id_product = pa.id_product
LEFT OUTER JOIN person p on pa.id_person = p.id_person
The subquery will give you the max end date by product and person. since we need the subquery to return the person, you would possibly get multiple id_person records for each product if you have multiple allocations that haven't expired. The "having" clause will use the already aggregated end date to match to the max end date by product without the person, so you will get only the "most future" allocation end date. Since the product allocation is joined to the person, and they are left joined to the product table, if there are no unexpired or future allocations, or there are no allocations at all, then you will get 'US' as your owner. you can always add in another table join directly from product if your "company" is somewhere else, and substitute that for 'US'.
I don't have a data set to work off of that exactly duplicates this, so I can't test it, but I did test the having process and it functioned correctly.
if there are other assumption like, only one allocation at a time, etc, this could be simplified. Also you could work this through a memory table with only current and future allocations by product, but I believe this should work. You may need to adjust the 'getdate()' if you are working off date only not date time. If that's the case then use cast(convert(varchar(10), getdate(), 101) as datetime) and return only the date portion of the getdate function.
Hope this helps you out

Best structure for inventory database

I want to create a small database for my inventory but I have some problems on picking a structure. The inventory will be updated daily at the end of the day.
The problem I am facing is the following.
I have a table for my products, having an
id, name, price, quantity.
Now I have another table for my sales, but there is my problem. What kind of fields do I need to have. At the end of the day I want to store a record like this:
20 product_x $ 5,00 $ 100,-
20 product_y $ 5,00 $ 100,-
20 product_z $ 5,00 $ 100,-
20 product_a $ 5,00 $ 100,-
-------------------------------------------------
$ 400,-
So how do I model this in a sales record. Do I just create a concatenated record with the product id's comma separated.
Or is there another way do model this the right way.
This is a model which supports many aspects,
Supports Sites, Locations and Warehouses etc.
Supports Categorization and Grouping
Support Generic Product (Ex. "Table Clock" and specific product "Citizen C123 Multi Alarm Clock" )
Also support Brand Variants (by various manufacturers)
Has CSM (color / size / model support) Ex. Bata Sandles (Color 45 Inch Blue color)
Product Instances with serials (such as TVs , Refrigerators etc.)
Lot control / Batch control with serial numbers.
Pack Size / UOM and UOM Conversion
Manufacturer and Brands as well as Suppliers
Also included example transaction table (Purchase order)
There are many other transaction types such as Issues, Transfers, Adjustments etc.
Hope this would help. Please let me know if you need further information on each table.
Cheers...!!!
Wajira Weerasinghe.
Sites
id
site_code
Site_name
Warehouse
id
site_id
warehouse_code
warehouse_name
Item Category
id
category_code
category_name
Item Group
id
group_code
group_name
Generic Product
id
generic_name
Product
id
product_code
category_id
group_id
brand_id
generic_id
model_id/part_id
product_name
product_description
product_price (current rate)
has_instances(y/n)
has_lots (y/n)
has_attributes
default_uom
pack_size
average_cost
single_unit_product_code (for packs)
dimension_group (pointing to dimensions)
lot_information
warranty_terms (general not specific)
is_active
deleted
product attribute type (color/size etc.)
id
attribute_name
product_attribute
id
product_id
attribute_id
product attribute value (this product -> red)
id
product_attribute_id
value
product_instance
id
product_id
instance_name (as given by manufacturer)
serial_number
brand_id (is this brand)
stock_id (stock record pointing qih, location etc.)
lot_information (lot_id)
warranty_terms
product attribute value id (if applicable)
product lot
id
lot_code/batch_code
date_manufactured
date_expiry
product attribute value id (if applicable)
Brand
id
manufacturer_id
brand_code
brand_name
Brand Manufacturer
id
manufacturer_name
Stock
id
product_id
warehouse_id, zone_id, level_id, rack_id etc.
quantity in hand
product attribute value id (if applicable) [we have 4 red color items etc.]
Product Price Records
product_id
from_date
product_price
Purchase Order Header
id
supplier_id
purchase_date
total_amount
Purchase Order Line
id
po_id
product_id
unit_price
quantity
Supplier
id
supplier_code
supplier_name
supplier_type
product_uom
id
uom_name
product_uom_conversion
id
from_uom_id
to_uom_id
conversion_rule
I'd have a table with a row per item per day - store the date, the item ID, the quantity sold, and the price sold at (store this even though it's also in the product table - if that changes, you want the value you actually sold at preserved). You can compute totals per item-day and totals per day in queries.
Tables:
create table product (
id integer primary key,
name varchar(100) not null,
price decimal(6,2) not null,
inventory integer not null
);
create table sale (
saledate date not null,
product_id integer not null references product,
quantity integer not null,
price decimal(6,2) not null,
primary key (saledate, product_id)
);
Reporting on a day:
select s.product_id, p.name, s.quantity, s.price, (s.quantity * s.price) as total
from product p, sale s
where p.id = s.product_id
and s.saledate = date '2010-12-5';
Reporting on all days:
select saledate, sum(quantity * price) as total
from sale
group by saledate
order by saledate;
A nice master report over all days, with a summary line:
select *
from (
(select s.saledate, s.product_id, p.name, s.quantity, s.price, (s.quantity * s.price) as total
from product p, sale s
where p.id = s.product_id)
union
(select saledate, NULL, 'TOTAL', sum(quantity), NULL, sum(quantity * price) as total
from sale group by saledate)
) as summedsales
order by saledate, product_id;
Try modelling your sales as a transaction - with a "header", i.e. who sold to, when sold, invoice # (if applicable), etc. and "line items", i.e. 20 * product_x # $5 = $100. The safest approach is to avoid relying upon prices etc. from the products table - as these will presumably change over time, and instead copy much of the product information (if not all) into your line item - so even when prices, item descriptions etc. change, the transaction information remains as was at the time the transaction was made.
Inventory can get quite complex to model. First you need to understand that you need to be able to tell the value of the inventory onhand based on what you paid for it. This means you cannot rely on a product table that is updated to the current price. While you might want such a table to help you figure out what to sell it for, there are tax reasons why you need to know the actual vlaue you paid for each item in the warehouse.
So first you need the product table (you might want to make sure you have an updated date column in this, it can be handy to know if your prices seem out of date).
Then you need a table that stores the actual warehouse location of each part and the price at purchase. If the items are large enough, you need a way to individually mark each item, so that you know what was taken out. Usually people use barcodes for that. This table needs to be updated to record that the part is no longer there when you sell it. I prefer to make the record inactive and have a link to my sales data to that record, so I know exactly what I paid for and what I sold each part for.
Sales should have at least two tables. One for the general information about the sale, the customername (there should also be a customer table most of the time to get this data from), the date, where it was shipped to etc.
Then a sales detail table that includes a record for each line item in the order. Include all the data you need about the part, color, size, quantity, price. This is not denormalizing, this is storing historical data. The one thing you do not want to do is rely on the prices in the product table for anything except the inital entry to this table. You do not want to do a sales report and have the numbers come out wrong becasue the product prices changed the day before.
Do not design an inventory database without consulting with an accountant or specialist in taxes. You also should do some reading on internal controls. It is easy to steal from a company undetected that has not done their work on internal controls in the database.
I think you need a table with fields showing the transaction properties per customer
OR
a table with fields - date, product(foreign), quantity - this way you'll have no problem with new products
Try multiple tables with links
table_products
id
name
table_product_sales
id
product_id
quantity
price_per
transaction_time AS DATETIME
SELECT table_product_sales.*, table_product.name
FROM table_product
JOIN table_product_sales
ON table_product_sales.product_id = table_product.id
GROUP BY DATE(transaction_time)
Haven't tried it but will something like that work? That allows you to keep each transactions separate so you can query things like average number sold per sale, total sold per date, total sales each day, etc.

Resources