SQL comparison report on cartesian product using subquery - sql

I'm a student building a comparison report query in MySQL on a database that tracks customers, products, and purchases in separate tables. I have to create a report that shows how many products were sold every month for each province using a subquery. I was told to use a cross join between product and customer, however, my query runs into a problem when I try to group them as the records all collapse into each other and I don't understand why this is happening. I'm not sure if this is the correct way to approach this problem since my customer and product table don't have any values that intersect with each other except through the purchase table.
These are my create table scripts
CREATE TABLE 'customer' (
'CustomerID' INT NOT NULL,
'City' VARCHAR(100) NOT NULL,
'Province' CHAR(2) NOT NULL,
PRIMARY KEY ('CustomerID'));
CREATE TABLE 'product' (
'ProductID' INT NOT NULL,
'ProductName' VARCHAR(100) NOT NULL,
'Price' DECIMAL(5,2) NOT NULL,
PRIMARY KEY ('ProductID'));
CREATE TABLE 'purchase' (
'PurchaseID' INT NOT NULL,
'PurchaseDate' DATE NOT NULL,
'customer_CustomerID' INT NOT NULL,
'product_ProductID' INT NOT NULL,
PRIMARY KEY ('PurchaseID'),
CONSTRAINT 'fk_purchase_customer'
FOREIGN KEY ('customer_CustomerID')
REFERENCES 'customer' ('CustomerID'),
CONSTRAINT 'fk_purchase_product'
FOREIGN KEY ('product_ProductID')
REFERENCES 'product' ('ProductID'));
This is the query that I have written as I have understood the instructions.
SELECT DISTINCT province, productName AS Product, JanTotalSales
FROM PRODUCT cross join CUSTOMER
LEFT JOIN
(
SELECT purchaseID, product_productID, customer_customerID, COUNT(purchaseDate) AS JanTotalSales
FROM PURCHASE
WHERE MONTH(purchaseDate) = 01
)JAN ON PRODUCT.productID = JAN.product_productID
GROUP BY province, productID;
I should be getting results like this
Province
Product
JanTotalSales
FebTotalSales
...
TotalSales
QC
Paper
1
NULL
...
1
ON
Paper
1
2
...
3
AB
Paper
1
NULL
...
1
AB
Wire
2
2
...
4
ON
Wire
2
1
...
3
NULL
Kit
NULL
NULL
...
NULL
SK
Gummy
1
1
...
2
NULL
Bag
NULL
NULL
...
NULL
However, I receive results like this when I do it on the January subquery.
Province
Product
JanTotalSales
AB
Paper
NULL
AB
Wire
NULL
AB
Kit
NULL
AB
Kit
13
ON
Paper
NULL
ON
Wire
NULL
ON
Kit
NULL
ON
Kit
13
I appreciate whatever help you can give to show me where I'm going wrong. From what I understand it's something to do with how the grouping occurs but I can't figure out why.

Related

Returning values using a query that have been awarded a bonus

I'm trying to transactionId that were randomly chosen to be awarded a bonus which is essentially just having their rewardValue be double.
Following is my schema:
CREATE TABLE rewards (
rewardId INTEGER PRIMARY KEY,
rewardType VARCHAR(20),
rewardValue NUMERIC(6,2)
);
CREATE TABLE deposit (
depositId INTEGER PRIMARY KEY,
depositDate DATE,
customerId INTEGER NOT NULL REFERENCES Customers
);
CREATE TABLE transactions (
transactionId SERIAL PRIMARY KEY,
depositId INTEGER NOT NULL UNIQUE REFERENCES Orders,
transactionAmount NUMERIC(18,2)
);
Here's my query:
select distinct t.transactionId
from transactions t join deposit d on t.depositId = d.depositId join
rewards r on 2 * r.rewardValue <= t.transactionAmount;
I get some output which is just a few values repeating over and over. Does anyone know how to fix this?
before answer your question, seems like your join have a issue.
if you want to find transactions eligible for rewards
try this ->
select distinct transactionId from
(select t.transactionId,r.rewardValue,t.transactionAmount
from transactions t join deposit d on t.depositId = d.depositId,rewards r ) tbl where 2*rewardValue <= transactionAmount

Joining column for multiple tables

I am trying to extract two same-data-type columns from two different tables using one query. NOTE: Accounts attribute length in both table varies. Union can't work here because number of columns are (in reality) different in both tables.
CREATE TABLE IF NOT EXISTS `mydb`.`TABLE_A` (
`ID_TABLE_A` INT NOT NULL AUTO_INCREMENT,
`ACCOUNT` VARCHAR(5) NULL,
`SALES` INT NULL,
PRIMARY KEY (`ID_TABLE_A`))
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `mydb`.`TABLE_B` (
`ID_TABLE_B` INT NOT NULL AUTO_INCREMENT,
`ACOUNT` VARCHAR(9) NULL,
`SALES` INT NULL,
PRIMARY KEY (`ID_TABLE_B`))
ENGINE = InnoDB;
Requirement:(I know this can't be right but just to demonstrate a partial picture)
SELECT
ACCOUNTS,
SALES
FROM
TABLE_A, TABLE_B
Result:
---------------
|accounts|sales|
| 2854 |52500 |
| 6584 |54645 |
| 54782| 5624 |
| 58496|46259 |
| 56958| 6528 |
---------------
If you want the union of two tables that are not union-compatible, then make them union-compatible:
(SELECT
ACCOUNTS,
SALES
FROM
TABLE_A) UNION ALL
(SELECT
ACCOUNTS,
SALES
FROM TABLE_B)
I put the UNION ALL based on the assumption that you would like to keep duplicates. If you would like the output to be duplicate-free, replace it with UNION.

How to Verify Multiple Foreign Key Combination in a table with a Primary Key in another table

I Have a Specification Master Table with three columns
(
ID BIGINT PRIMARY KEY,
Name varchar,
Value varchar
)
a Product Master Table with two Columns
(
ID BIGINT PRIMARY KEY,
Name varchar
)
a Stock table with
(
StockID BIGINT PRIMARY KEY,
ProductID BIGINT FOREIGN KEY (Product.ID),
SpecGroupID BIGINT UNIQUE KEY,
Stock INT
)
a Specification Grouping Table with
(
GroupID BIGINT FOREIGN KEY (Stock.SpecGroupID),
SPecificationID BIGINT FOREIGN KEY (Specification.ID),
PRIMARY KEY (Composite)
)
Now I am looking for a combination of specification if it has any stock or not.
but could not find a logic to match exact combination.
The problem I am facing if a combination of specification has n specification associated with a stock.SpecGroupID in Specification Grouping Table.
While I am searching with a few less than those n specification combination it always returning the same SpecGroupID for n specs group.
Imagine I have a apple (Color: Red; Size:5; Weight:10) in stock
And someone is ordering for a apple (Color: Red; Size:5)
I Need to give a result: Not Available
Will this work for you?
SELECT Specification.ID SpecId
, Specification.Name SpecificationName
, Value SpecificationValue
, MAX(ISNULL(Stock.Stock,0)) HasStock
FROM
Specification
LEFT JOIN SpecificationGrouping sg
ON sg.SpecificationID = Specification.ID
LEFT JOIN Stock
ON GroupID = SpecGroupID
GROUP BY Specification.ID
, Specification.Name
, Value
First cte Groups tell me the specifications of each group.
Second cte GroupDetail join all properties in a single string like this Color|Red,Size|5,Weight|10
Third cte OrderDetail is just the test input. in this sample 6 and 9 are orders.
Last query. Try to find one item on stocks with exact detail as the order
If a specification isn't on stocks I assume is an order, here 6 doesn't have item on stock so produce all null and 9 does have stock and return the StockValue.
SQL Fiddle Demo
WITH Groups as
(
SELECT *
FROM SpecificationGrouping SG
INNER JOIN Specification S
ON Sg.SPecificationID = S.ID
),
GroupDetail as
(
Select distinct ST2.GroupID,
substring(
(
Select ','+ ST1.Name + '|' + ST1.Value AS [text()]
From Groups ST1
Where ST1.GroupID = ST2.GroupID
ORDER BY ST1.Name
For XML PATH ('')
), 2, 1000) [Detail]
From Groups ST2
),
OrderDetail as
(
SELECT Detail
FROM GroupDetail
WHERE GroupID = 9
)
SELECT S.SpecGroupID, S.Stock, G.[Detail]
FROM Stock S
INNER JOIN GroupDetail G
ON S.SpecGroupID = G.GroupID
RIGHT JOIN OrderDetail O
ON O.Detail = G.Detail
Output for 9
| SpecGroupID | Stock | Detail |
|-------------|-------|----------------------------|
| 3 | 5 | Color|Red,Size|5,Weight|10 |

In SQL, how to format values so I have column1value.column2value

I am new to SQL and have some problems with formatting. In column1, I have categories living, food, transportation and in each category, I have subcategories. For example, food.lunch, food, grocery.
Now I want to have a new column called detailed expenses in which I want to display CATEGORY.SUBCATEGORY
for example, food.lunch
How can I achieve this? Any help?
You need to structure your table like this (not an OOP approach)
categories
-----------------------
id name parent
1 food 0
2 lunch 1
...
3 category 0
4 subcategory 3
How about this idea: You can have a table Expenses that contains records to identify category and subcategory with an amount. Then you can total (SUM) the amounts by category or subcategory.
SELECT Category.Name,SubCategory.Name,SUM(Expense.Amount)
FROM Expense ex INNER JOIN Category cat on (ex.CategoryId = cat.Id)
INNER JOIN SubCategory subcat on (ex.SubCategoryId = subcat.Id)
GROUP BY cat.Name, subcat.Name,ex.Amount
CREATE TABLE Category(
Id int NOT NULL PRIMARY KEY,
Name varchar(50) NOT NULL
PRIMARY KEY (Id),
)
CREATE TABLE SubCategory(
Id int NOT NULL PRIMARY KEY,
CategoryId int,
Name varchar(50) NOT NULL
PRIMARY KEY (Id),
FOREIGN KEY (CategoryId) REFERENCES Category(Id))
CREATE TABLE Expense(
ID NOT NULL PRIMARY KEY,
CategoryId int,
SubCategoryId int,
Amount money
PRIMARY KEY (Id),
FOREIGN KEY (CategoryId) REFERENCES Category(Id)
FOREIGN KEY (SubCategoryId) REFERENCES SubCategory(Id))

Mysql selecting rows from multiple tables

I'm working on a catalog site where users can browse categories. Categories can contain other categories and products, and products can belong to more than one category. The relevant database schema looks something like this:
CREATE TABLE products (
product_id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
product_title VARCHAR(100) NOT NULL,
product_status TINYINT UNSIGNED NOT NULL
);
CREATE TABLE product_categories (
category_id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
parent_category_id INT UNSIGNED NOT NULL,
category_title VARCHAR(100) NOT NULL,
category_status TINYINT UNSIGNED NOT NULL,
category_order INT UNSIGNED NOT NULL
);
CREATE TABLE products_categories (
product_id INT UNSIGNED NOT NULL,
category_id INT UNSIGNED NOT NULL,
product_order INT UNSIGNED NOT NULL,
PRIMARY KEY(product_id, category_id)
);
The issue i'm having is I need to paginate the results using LIMIT n, n:
$perpage = 20;
$start = (isset($_GET['page'])) ? (int)$_GET['page'] * $perpage : 1;
$limitsql = "LIMIT $start, $perpage";
But I can't figure out how to select both distinct categories and products without joining and merging the results. Ideally I would like results like this:
product_id | product_title | category_id | category_title
NULL | NULL | 32 | category foo
NULL | NULL | 239 | category bar
9391 | product foo | NULL | NULL
325 | product bar | NULL | NULL
The best I've been able to do is get something like this, which doesn't really help:
product_id | product_title | category_id | category_title
9391 | product foo | 32 | category foo
325 | product bar | 239 | category bar
239 | product foo2 | 32 | category foo
115 | product bar2 | 239 | category bar
The only other solutions that I can think of would be to query all subcategories and products within the category, stick them in a php array and extract the current page with array_slice. Considering the volume of products (several thousand) this isn't a very appealing option.
Otherwise I could query the number of categories, and offset the $start in the LIMIT clause by the number of categories. This get's messy though if there is more than a full page of categories.
Here is my current working query which gives me the results above:
SELECT
p.product_id, p.product_title,
c.category_id, c.category_title
FROM products AS p
JOIN product_categories AS c
ON c.parent_category_id='20'
INNER JOIN products_categories AS pc
ON p.product_id=pc.product_id
WHERE p.product_status='1' AND pc.category_id='20'
ORDER BY pc.product_order ASC
Edit
I think i've got it working with UNION, which I completely forgot about
SELECT
c.category_id AS row_id, c.category_title AS row_title, 1 AS is_category
FROM product_categories AS c
WHERE c.parent_category_id='20'
UNION
SELECT
p.product_id AS row_id, p.product_title AS row_title, 0 AS is_category
FROM products AS p
INNER JOIN products_categories AS pc
ON p.product_id=pc.product_id
Edit 2
I guess Union isn't going to work as I thought. Since both are treated as separate queries I can't apply LIMIT to the entire result, only each individual SELECT. Also it seems the columns selected from each statement must be of the same type of the corresponding type in the other statement.
Use:
SELECT *
FROM (SELECT c.category_id AS row_id, c.category_title AS row_title, 1 AS is_category
FROM product_categories AS c
WHERE c.parent_category_id='20'
UNION
SELECT p.product_id AS row_id, p.product_title AS row_title, 0 AS is_category
FROM products AS p
JOIN products_categories AS pc ON p.product_id=pc.product_id) x
LIMIT x, y
Another way you could approach this would be changing your schema to make categories and products the same thing essentially.
CREATE TABLE items (
item_id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
item_title VARCHAR(100) NOT NULL,
item_status TINYINT UNSIGNED NOT NULL,
category_or_item TINYINT UNSIGNED NOT NULL,
);
CREATE TABLE items_parents (
item_id INT UNSIGNED NOT NULL,
parent_id INT UNSIGNED NOT NULL, #points to itemid
item_order INT UNSIGNED NOT NULL,
PRIMARY KEY(item_id, parent_id)
);
Your query then is flat and you can sort it by category_or_item so categories appear first.