SQL groupBY with new columns creation - php

I need help, just stucked here.
I have matches table.
id product_id property_id
1 1 1
2 1 2
3 1 3
4 1 4
5 2 1
6 2 2
7 2 3
8 3 1
9 3 2
10 3 3
11 3 4
12 3 5
What I need to have in result of sql query is:
product_id 1 2 3 4 5
1 1 2 3 4 null
2 1 2 3 null null
3 1 2 3 4 5
Thanks a lot in advance.
Additional. So far I tried another way, but it's too complicated. I cross joined filtered properties to obtain a result.
SELECT t1.id, t1.product_id, t1.property_id as pr_id1, t2.property_id as pr_id2
FROM
(SELECT * FROM matches WHERE (property_id = 25 OR property_id = 39)) t1
CROSS JOIN
(SELECT * FROM matches WHERE (property_id = 29)) t2
ON t1.product_id = t2.product_id

select
product_id,
case when sum(case when property_id = 1 then 1 else 0 end )>0 then 1 end '1',
case when sum(case when property_id = 2 then 1 else 0 end )>0 then 2 end '2',
case when sum(case when property_id = 3 then 1 else 0 end )>0 then 3 end '3',
case when sum(case when property_id = 4 then 1 else 0 end )>0 then 4 end '4',
case when sum(case when property_id = 5 then 1 else 0 end )>0 then 5 end '5'
from
t
group by product_id
http://sqlfiddle.com/#!9/ce02aa/3/0

Thank you all for help, it gave me couple hints.
I found alternative way to do this with crossjoins.
Basically I crossjoin as much tables as I have attributes / properties pairs.
SELECT t1.id,
t1.subproduct_id,
t1.property_id as pr_id1,
t2.property_id as pr_id2,
t3.property_id as pr_id3,
FROM
(SELECT * FROM subproduct_attributes WHERE (property_id = 25 OR property_id = 30)) t1
CROSS JOIN
(SELECT * FROM subproduct_attributes WHERE (property_id = 28)) t2
ON t1.subproduct_id = t2.subproduct_id
CROSS JOIN
(SELECT * FROM subproduct_attributes WHERE (property_id = 27)) t3
ON t1.subproduct_id = t3.subproduct_id
Here is the code of laravel implementation.
$query = $this->builder->Join('subproduct_attributes AS first', 'first.subproduct_id', '=', 'subproducts.id')
->select('subproducts.*',
DB::raw('first.subproduct_id AS sub_id'),
DB::raw('first.property_id AS prop_id'));
$previous_table = 'first';
$i = 1;
foreach ($array as $slug => $attribute) {
$keys = [];
foreach ($attribute as $property_key => $value) {
// Quick check for property existence
if (AttributeProperty::find($property_key)) {
$keys[] = $property_key;
}
}
// if it is first iteration do not crossjoin
if ($i == 1) {
$query->whereIn('first.property_id', $keys);
} else {
$query->crossJoin("subproduct_attributes AS $slug", function ($join) use ($keys, $slug, $previous_table) {
$join->on("$slug.subproduct_id", '=', "$previous_table.subproduct_id")
->whereIn("$slug.property_id", $keys);
});;
$previous_table = $slug;
}
$i++;
}

Related

Looking for MySQL/MariaDB where query over muliple rows

Table Products
ad_id| property_id | property_value_id
69 4 1
69 7 6
69 6 3
67 7 6
...
For Example:
I need to search ((where property_id = 4 and property_value_id = 1) and (where property_id = 7 and property_value_id = 6))
Result should be: 69
The values im searching for are dynamic.
SELECT DISTINCT ad_id
FROM
(SELECT ad_id
FROM Producst
WHERE (property_id = 4 AND property_value_id = 1)
OR (property_id = 7 AND property_value_id = 6)
OR (property_id = 6 AND property_value_id = 3)
group by ad_id
having count(ad_id) = 3) as subquery
Should do the job.
You can join the table with itself three times, one for each pair of searched value. The query can take the form:
select distinct a.ad_id
from my_table a
join my_table b on b.ad_id = a.ad_id
join my_table c on c.ad_id = a.ad_id
where a.property_id = 4 and a.property_value_id = 1
or b.property_id = 7 and b.property_value_id = 6
or c.property_id = 6 and c.property_value_id = 3

count id, insert, then update by number range in another columns in same table mysql

I am trying to update levels in db like this:
"count (id) from users table where referral_id = id As aggref set agg_referral = aggref AND level = 1 if aggref in between 1 to 3"
So far i know but no success :(
Here is my db structure
users table:
id (AI) name Referral id agg_referral Level
=========== ========== ================== ============== ======
1 user A 0 0 0
2 user B 3 0 0
3 user C 1 0 0
4 user D 3 0 0
Here is my code i am running it using cron job:
<?php
include( $_SERVER['DOCUMENT_ROOT'] . '/config.php' );
mysql_connect(DB_HOST,DB_USER,DB_PASS);
mysql_select_db(DB_NAME);
$query="UPDATE users SET agg_referral = (SELECT COUNT(id) from users WHERE users.referral_id = users.id)";
mysql_query($query);
?>
I am expecting results like this
id (AI) name Referral id agg_referral Level
=========== ========== ================== ============== ======
1 user A 0 0 0
2 user B 3 0 0
3 user C 1 2 1
4 user D 3 0 0
You can use an update join:
update users u1
join (
select referral_id,
count(*) cnt
from users
group by referral_id
) u2 on u1.id = u2.referral_id
set u1.agg_referral = u2.cnt,
u1.level = case
when cnt < 1
then 0
else ceil((- 5 + sqrt(25 + 8 * cnt)) / 2)
end;
Demo
It finds times an id is referred and then join it with the table to update the counts and level.
The case statement produces 0 for cnt = 0 or less, 1 for cnt between 1 and 3, 2 for cnt between 4 and 7 and . . so on.
To learn more on how the formula works, see https://math.stackexchange.com/questions/2171745/find-group-number-for-a-given-number-from-groups-of-increasing-size
EDIT:
If your initial group size is value other than 3, use this to get the required equation:
ceil((1 - 2 * g + sqrt(4 * g * g + 1 - 4 * g + 8 * cnt)) / 2)
where g is the intial size of the group.
For g = 3, it solves to:
ceil( -5 + sqrt(25 + 8 * cnt) / 2 )
for g = 10, it solves to:
ceil( -19 + sqrt(361 + 8 * cnt) / 2 )
On how this function is made, see the link provided above.
In MySQL, you can do this by counting the number of referrals for each id using a subquery. Then use that information in the update:
UPDATE users u JOIN
(SELECT referral_id, COUNT(*) as cnt
FROM users u
GROUP BY referral_id
) ur
ON u.id = ur.referral_id
SET u.agg_referral = ur.cnt,
u.level = (CASE WHEN ur.cnt BETWEEN 1 and 3 THEN 2 ELSE level END);

Fetch row with different data on same column

We have the table structure like
Fieldid userid data
41 2 1
12 2 0
41 3 1
12 3 1
We want the user which have (data=1 and filedid = 41) and (data = 0 and filedid=12)
We tried the query
select userid from table
where (data=1 and filedid = 41)
AND (data = 0 and filedid=12)
but it returns empty results.
Can anyone help how we will get the record using MySQL?
You can use grouping with a conditional aggregate in the HAVING clause:
SELECT userid
FROM mytable
GROUP BY userid
HAVING COUNT(CASE WHEN data = 1 AND FieldId = 1 THEN 1 END) >= 1 AND
COUNT(CASE WHEN data = 0 AND FieldId = 12 THEN 1 END) >= 1
you need to use union all like:
select * from table1 where Fieldid = 41 and data = 1 union all
select * from table1 where Fieldid = 12 and data = 0
attached image shows the output
Similar to #Giorgos, with multicolumn compare for conciseness:
select userid
from t
group by userid
having sum((data,fieldId) = (1, 41)) > 0
and sum((data,fieldId) = (0, 12)) > 0
When searching for results with one or another set of data you have to use OR keyword, not AND.
SELECT userid FROM table
WHERE (data = 1 AND filedid = 41)
OR (data = 0 AND filedid = 12)

How to get the latest 5 company data from two tables

I have 2 tables in mysql database.
a) company
cid company_name
===================
1 AstraZeneca
2 Emirates
3 Development Bank of Singapore
4 Royal Copenhagen
5 xxx
6 xxx
2) history
hid user_id view_id is_save mark_as view date
==============================================================
1 2 2 0 3 2016-08-25 22:06:12
2 3 3 1 3 2016-08-25 22:07:12
3 3 3 0 1 2016-08-25 22:08:12
4 3 2 0 1 2016-08-25 22:09:12
5 2 4 0 1 2016-08-25 22:10:12
6 4 5 0 1 2016-08-25 22:11:12
7 4 6 0 1 2016-08-25 22:12:12
This view_id is containing cid value.
Now, always I want to show latest 5 company_name from company table as ascending order based on history table view_id.
For that purpose I am doing following query. But company_name is not showing either ASC or DESC order
Here is the query :
$getViewID3 = mysqli_query($link, "SELECT view_id, hid, is_save FROM history WHERE user_id = '$user_id' AND mark_as = 3 GROUP BY view_id ORDER BY view_date DESC LIMIT 5 ");
if(mysqli_num_rows($getViewID3) > 0 ) {
while( $fetchViewId3 = mysqli_fetch_array($getViewID3) ) {
$viewid3 = (int) $fetchViewId3['view_id'];
$hid3 = (int) $fetchViewId3['hid'];
$is_save3 = (int) $fetchViewId3['is_save'];
$getCompany = mysqli_query($link, "SELECT company_name FROM company WHERE cid = '$viewid3' ORDER BY company_name DESC");
if(mysqli_num_rows($getCompany) > 0 ) {
while ($fetchCompany2 = mysqli_fetch_array($getCompany)) {
$cName = htmlspecialchars($fetchCompany2['company_name']);
$url_link = "{$url}company.php?cid=$viewid";
if($is_save3 == 1) {
$checked = 'checked = "checked"';
} else {
$checked = '';
}
echo "<li><a onClick='window.document.location=\"$url_link\"'> $cName </a> <input type='checkbox' class='data_save' $checked data-hid='$hid' data-saveid='$viewid3' name='save_history'></li>";
}
}
}
For example : Result is showing : A, E, D, R, L letter order.
It's should be show : A, D, E, L, R letter Order from company_name column.
If I didn't misunderstand:
SELECT
C.company_name
FROM company C
INNER JOIN
(
SELECT
view_id,
MAX(view_date) max_view_date
FROM history
WHERE is_save IN (0,1) AND mark_as = 3
GROUP BY view_id
ORDER BY max_view_date DESC
LIMIT 5
) AS t
ON C.cid = t.view_id
ORDER BY C.company_name ASC;
Note:
Since you want latest 5 companies the following query will put the last view_date beside the view_id.
Now if you sort these rows based on descending order of max_view_date and later limit the result to 5 then you will get at most five view_ids from the inner query.
Later a simple INNER JOIN between this result set and your company table will finish the job.
Sorry, sorting the final result in ascending order of company name will finish the job.
EDIT:
In order to get all the columns from history table and company_name column from company table:
SELECT
C.company_name,
t.*
FROM company C
INNER JOIN
(
SELECT
history.*
FROM history
INNER JOIN
(
SELECT
view_id,
MAX(view_date) max_view_date
FROM history
WHERE is_save IN (0,1) AND mark_as = 3
GROUP BY view_id
ORDER BY max_view_date DESC
LIMIT 5
) AS latestHistory
ON history.view_id = latestHistory.view_id AND history.view_date = latestHistory.max_view_date
) AS t
ON C.cid = t.view_id
ORDER BY C.company_name ASC;

Mysql Where Column A = X and Column B = Y and or Column B = Z

I'm trying to get a set of values from a pivot table where column A is equal to an array of values, so for example ID 12 has attribute_value_id equal to 3 and 9. Can this be done? I've got this far...
ID | post_id | attribute_id | attribute_value_id
8 12 1 3
9 12 2 13
10 13 1 3
11 13 2 9
12 16 1 3
13 16 2 9
88 11 1 1
89 11 2 8
90 11 3 18
91 11 4 22
The query...
select *
from `searching_for_posts`
where (
select count(*)
from `attributes`
inner join `searching_for_attributes`
on `attributes`.`id` = `searching_for_attributes`.`attribute_id`
where `searching_for_attributes`.`searching_for_post_id` = `searching_for_posts`.`id`
and (`attribute_value_id` = 3 and `attribute_value_id` = 9)
) >= 1
If I use the and then I get no values. If I use the or then I get 3 values but it should return 2. I have limited SQL experience.
You can do this using group by and having. Your logic is hard to follow, but it is something like this:
select post_id
from table t
where attribute_value_id in (3, 9)
group by post_id
having count(distinct attribute_id) = 2;
I would think you would want to check on attribute_id as well, but that doesn't seem to be part of the question.
EDIT:
If these are stored in another table:
select a.post_id
from attributes a join
searching_for_attributes sfa
on a.attribute_id = sfa.attribute_id and
a.attribute_value_id = sfa.attribute_value_id
group by a.post_id
having count(*) = (select count(*) from searching_for_attributes);
In response to #GordonLinoff answer, I've managed to use GROUP BY and HAVING COUNT to get the desired data. Here's what I came up with and hope this helps someone else...
select *
from `searching_for_posts`
where (
select count(*)
from `attributes`
inner join `searching_for_attributes` on `attributes`.`id` = `searching_for_attributes`.`attribute_id`
where `searching_for_attributes`.`searching_for_post_id` = `searching_for_posts`.`id`
and `attribute_value_id` in (3, 9)
having count(distinct `attributes`.`id`) = 2
) >= 1
group by `id`

Categories