Find in hasMany where all Conditions are true - cakephp-2.0

I have this tables:
products -> hasMany -> categories
Table: products
+----+--------+
| id | title |
+----+--------+
| 1 | Prod 1 |
| 2 | Prod 2 |
| 3 | Prod 3 |
+----+--------+
Table: categories
+----+-------+------------+
| id | title | product_id |
+----+-------+------------+
| 1 | Cat 1 | 1 |
| 2 | Cat 2 | 1 |
| 3 | Cat 3 | 2 |
| 4 | Cat 1 | 1 |
+----+-------+------------+
How can i query the products which are in both categories "Cat 1" AND "Cat 2" in my example i want only find "Prod 1"

My intuition is that you should change the database, so you instead have a habtm relation.
So your tables should looks like this:
Products (id, title)
ProductCategories (product_id, category_id)
Categories (id, title)
So the product can belong to many categories.
Then you can look up your table for products belonging to category with ID = 1 and category with ID = 3, by doing following query:
MySQL query
SELECT Products.id, Products.title
FROM Products
WHERE
Products.id IN
(
SELECT c1.product_id
FROM ProductCategories AS c1
INNER JOIN ProductCategories AS c2
ON c1.product_id = c2.product_id
WHERE c1.category_id = 1 AND c2.category_id = 3
);

Related

Get all categories with number of associated records with where clause

So I have two tables:
Categories
-------------------
| Id | Name |
-------------------
| 1 | Category1 |
-------------------
| 2 | Category2 |
-------------------
| 3 | Category3 |
-------------------
Products
--------------------------------------------
| Id | CategoryId | Name | CreatedDate |
--------------------------------------------
| 1 | 1 | Product1 | 2017-05-05 |
--------------------------------------------
| 1 | 1 | Product2 | 2017-05-06 |
--------------------------------------------
| 2 | 2 | Product3 | 2017-12-21 |
--------------------------------------------
I need a query to select all categories along with the number of products for each for a specific time range in which those products were created (CreatedDate).
What I currently have is this:
SELECT c.[Name], COUNT(p.[Id]) AS ProductCount
FROM Categories AS c
LEFT JOIN Products AS p ON p.[CategoryId] = c.[Id]
WHERE p.[CreatedDate] BETWEEN '2017-05-01' AND '2017-06-01'
GROUP BY c.[Name]
My issue is that I'm not seeing Category2 and Category3 in the results set because they don't pass the WHERE clause. I want to see all categories in the results set.
Put the where condition in the left join clause
SELECT c.[Name], COUNT(p.[Id]) AS ProductCount
FROM Categories AS c
LEFT JOIN Products AS p ON p.[CategoryId] = c.[Id]
AND p.[CreatedDate] BETWEEN '2017-05-01' AND '2017-06-01'
GROUP BY c.[Name]
This way it is applied to the join only and not to the complete result set.

How do you create a query which returns dynamic column names in Postgresql?

I have two tables in a reporting database, one for orders, and one for order items. Each order can have multiple order items, along with a quantity for each:
Orders
+----------+---------+
| order_id | email |
+----------+---------+
| 1 | 1#1.com |
+----------+---------+
| 2 | 2#2.com |
+----------+---------+
| 3 | 3#3.com |
+----------+---------+
Order Items
+---------------+----------+----------+--------------+
| order_item_id | order_id | quantity | product_name |
+---------------+----------+----------+--------------+
| 1 | 1 | 1 | Tee Shirt |
+---------------+----------+----------+--------------+
| 2 | 1 | 3 | Jeans |
+---------------+----------+----------+--------------+
| 3 | 1 | 1 | Hat |
+---------------+----------+----------+--------------+
| 4 | 2 | 2 | Tee Shirt |
+---------------+----------+----------+--------------+
| 5 | 3 | 3 | Tee Shirt |
+---------------+----------+----------+--------------+
| 6 | 3 | 1 | Jeans |
+---------------+----------+----------+--------------+
For reporting purposes, I'd love to denormalise this data into a separate PostgreSQL view (or just run a query) that turns the data above into something like this:
+----------+---------+-----------+-------+-----+
| order_id | email | Tee Shirt | Jeans | Hat |
+----------+---------+-----------+-------+-----+
| 1 | 1#1.com | 1 | 3 | 1 |
+----------+---------+-----------+-------+-----+
| 2 | 2#2.com | 2 | 0 | 0 |
+----------+---------+-----------+-------+-----+
| 3 | 3#3.com | 3 | 1 | 0 |
+----------+---------+-----------+-------+-----+
ie, it's a sum of the quantity of each item within the order with the product name; and the product names set as the column titles. Do I need to use something like crosstab to do this, or is there a clever way using subqueries even if I don't know the list of distinct product names at before the query runs.
This is one possible answer:
create table orders
(
orders_id int PRIMARY KEY,
email text NOT NULL
);
create table orders_items
(
order_item_id int PRIMARY KEY,
orders_id int REFERENCES orders(orders_id) NOT NULL,
quantity int NOT NULL,
product_name text NOT NULL
);
insert into orders VALUES (1, '1#1.com');
insert into orders VALUES (2, '2#2.com');
insert into orders VALUES (3, '3#3.com');
insert into orders_items VALUES (1,1,1,'T-Shirt');
insert into orders_items VALUES (2,1,3,'Jeans');
insert into orders_items VALUES (3,1,1,'Hat');
insert into orders_items VALUES (4,2,2,'T-Shirt');
insert into orders_items VALUES (5,3,3,'T-Shirt');
insert into orders_items VALUES (6,3,1,'Jeans');
select
orders.orders_id,
email,
COALESCE(tshirt.quantity, 0) as "T-Shirts",
COALESCE(jeans.quantity,0) as "Jeans",
COALESCE(hat.quantity, 0) as "Hats"
from
orders
left join (select orders_id, quantity from orders_items where product_name = 'T-Shirt')
as tshirt ON (tshirt.orders_id = orders.orders_id)
left join (select orders_id, quantity from orders_items where product_name = 'Jeans')
as jeans ON (jeans.orders_id = orders.orders_id)
left join (select orders_id, quantity from orders_items where product_name = 'Hat')
as hat ON (hat.orders_id = orders.orders_id)
;
Tested with postgresql. Result:
orders_id | email | T-Shirts | Jeans | Hats
-----------+---------+----------+-------+------
1 | 1#1.com | 1 | 3 | 1
2 | 2#2.com | 2 | 0 | 0
3 | 3#3.com | 3 | 1 | 0
(3 rows)
Based on your comment, you can try to use tablefunc like this:
CREATE EXTENSION tablefunc;
SELECT * FROM crosstab
(
'SELECT orders_id, product_name, quantity FROM orders_items ORDER BY 1',
'SELECT DISTINCT product_name FROM orders_items ORDER BY 1'
)
AS
(
orders_id text,
TShirt text,
Jeans text,
Hat text
);
But I think you are thinking the wrong way about SQL. You usually know which rows you want and have to tell it SQL. "Rotating tables" 90 degrees is not part of SQL and should be avoided.

Select query with only a single record in a mapping table

Please, can someone help me with what is possibly a simple query?
We have two tables with below structure.
Customer table:
+----+-----------+
| id | name |
+----+-----------+
| 1 | customer1 |
| 2 | customer2 |
| 3 | customer3 |
+----+-----------+
Customer role mapping table:
+-------------+-----------------+
| customer_id | customerRole_id |
+-------------+-----------------+
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 1 |
+-------------+-----------------+
I want to select customers with role id 1 only NOT with role id 1 AND 2.
So, in this case, it would be customer id 2,3,4 & 5. ignoring 1 as it has multiple roles.
Is there a simple query to do this?
Many thanks, for any help offered.
Hmmm, there are several ways to do this.
select c.*
from customers c
where exists (select 1 from mapping m where m.customerid = c.id and m.role = 1) and
not exists (select 1 from mapping m where m.customerid = c.id and m.role <> 1);
If you just want the customer id, a perhaps simpler version is:
select customerid
from mapping
group by customerid
having min(role) = 1 and max(role) = 1;
This solution assumes that role is never NULL.

How To implement inner join on self dependent table

Categories
1 | Pen | 3
2 | Book | 3
3 | Education | null
4 | Shirt | null
Product
1 | 10.00 | Parker-Pen | the description | 1000 | 1
2 | 35.00 | Dairy | the description | 500 | 2
3 | 9.00 | Dux-Pen | the description | 1000 | 1
4 | 350.00 | GeographyMap | the description | 30 | 3
4 | 250.00 | PoloShirt | the description | 100 | 4
These are the tables which I was actually retrieving the product whose category id is 3.
Here is the query which i used to retrive the data
select p.name, c.name
from product p
inner join Categories c on p.Categories_id=c.id
inner join Categories c2 on c2.id=3 or c2.parent=3
It is actually retrieving the data but in multiple time. And also have the poloshirt, which is not on the category id.
Can you explain me what is the problem and what is the better way for categorizing the product
If there is only one level of hierarchy allowed, you should get an answer with such query:
select p.name, c.name
from product p
inner join Categories c on p.Categories_id=c.id
where c.id=3 or c.parent=3

Random Join in Sql Server

I have two table that called Product and ProductImage.
There is 1-n relation between two table. One product and more than one image depending on the product.
I want to create a product view and I want to get one image randomly from ProductImage table for each product.
Example Data : http://sqlfiddle.com/#!6/43c69
I want something like below.
+-----------+------+-------------+
| ProductId | Name | WebPath |
+-----------+------+-------------+
| 1 | Foo | foowebpath2 |
| 2 | Boo | boowebpath3 |
| 3 | Zoo | zoowebpath1 |
+-----------+------+-------------+
or
+-----------+------+-------------+
| ProductId | Name | WebPath |
+-----------+------+-------------+
| 1 | Foo | foowebpath1 |
| 2 | Boo | boowebpath1 |
| 3 | Zoo | zoowebpath6 |
+-----------+------+-------------+
or
+-----------+------+-------------+
| ProductId | Name | WebPath |
+-----------+------+-------------+
| 1 | Foo | foowebpath4 |
| 2 | Boo | boowebpath2 |
| 3 | Zoo | zoowebpath5 |
+-----------+------+-------------+
or etc...
It have to be different each time.
You should try this
SELECT *, (SELECT TOP 1 WebPath FROM ProductImage PI
WHERE PI.ProductId = P.ProductId order by NEWID() ) as WebPart from Product P
Check this fiddle http://sqlfiddle.com/#!6/43c69/16
WITH Image AS
(
SELECT *, RAND(ProductImageId) R
FROM ProductImage
)
SELECT p.*, i2.* FROM Product P
INNER JOIN
(SELECT ProductId, MIN(R) R
FROM Image
GROUP BY ProductId) i1 ON i1.ProductId = p.ProductId
INNER JOIN Image i2 ON i2.R = i1.R

Resources