Is it possible to convert this kind of SQL into ActiveRecord query in Yii2:
SELECT a.id,
case length(a.id)
when 6 then (select region_name from region where id = concat(left(a.id,2),'0000'))
when 5 then (select region_name from region where id = concat(left(a.id,1),'0000'))
end as prov,
case length(a.id)
when 6 then (select region_name from region where id = concat(left(a.id,4),'00'))
when 5 then (select region_name from region where id = concat(left(a.id,3),'00'))
end as kab,
(select region_name from region where id = a.id) as kec
FROM region as a
WHERE a.region_name LIKE '%kamb%'
Absulatly convert this into query builde
$query = new \yii\db\Query;
$query->from("region as a ");
$query->select(new \yii\db\Expression("a.id,
case length(a.id)
when 6 then (select region_name from region where id = concat(left(a.id,2),'0000'))
when 5 then (select region_name from region where id = concat(left(a.id,1),'0000'))
end as prov,
case length(a.id)
when 6 then (select region_name from region where id = concat(left(a.id,4),'00'))
when 5 then (select region_name from region where id = concat(left(a.id,3),'00'))
end as kab,
(select region_name from region where id = a.id) as kec"));
$query->andWhere(['like', 'a.region_name', 'kamb']);
echo $query->createCommand()->rawSql;
In case of ActiveRecord query replace select section as above
Related
I have two database tables customers which contains data about customers with the scheme like that:
mysql> SELECT * FROM customers;
customer_id created_at partner_id
1 "2019-08-20 09:17:58" cats
2 "2019-09-12 11:46:37" dogs
and customers_facts which keeps the customers facts in a form of fact_name and corresponding fact_value.
mysql> SELECT * FROM customers_facts;
customer_id fact_name fact_value
1, name Milton
1 city Milan
2 surname Bloom
2 name Orlando
I want to create a pivot table which in each row will have a customer and it's facts each as a separate column. Something like this:
mysql> SELECT * FROM pivot_table;
customer_id created_at partner_id name city surname
1 "2019-08-20 09:17:58" cats Milton Milan
2 "2019-09-12 11:46:37" dogs Orlando Bloom
I've found a script that allows me to create such table:
SET #sql = '';
SELECT
#sql := CONCAT(#sql,if(#sql='','',', '),temp.output)
FROM
(
SELECT
DISTINCT
CONCAT(
'MAX(IF(cf.fact_name = ''',
fact_name,
''', cf.fact_value, NULL)) AS ''',
fact_name,
''''
) as output
FROM
customers_facts
) as temp;
SET #sql = CONCAT('SELECT c.customer_id, c.created_at, c.partner_id, ', #sql, '
FROM customers c
LEFT JOIN customers_facts AS cf
ON cf.customer_id = c.customer_id
GROUP BY c.customer_id, c.created_at, c.partner_id');
but I have an issue of how to make it so:
a) I will be able to query the pivot table
b) When I add a new entry / update an old one in one of those two original tables the pivot table will be updated
How to solve ? Is it possible ?
Consider the following:
DROP TABLE IF EXISTS customers;
CREATE TABLE customers
(customer_id SERIAL PRIMARY KEY
,created_at DATETIME NOT NULL
,partner_id INT NOT NULL
);
INSERT INTO customers VALUES
(1,"2019-08-20 09:17:58",108),
(2,"2019-09-12 11:46:37",110);
DROP TABLE IF EXISTS customers_facts ;
CREATE TABLE customers_facts
(customer_id INT NOT NULL
,fact_name VARCHAR(20) NOT NULL
,fact_value VARCHaR(20) NOT NULL
,PRIMARY KEY(customer_id,fact_name)
);
INSERT INTO customers_facts VALUES
(1,'name','Milton'),
(1,'city','Milan'),
(2,'surname','Bloom'),
(2,'name','Orlando');
Now we can create a VIEW in the manner you describe...
DROP VIEW IF EXISTS my_pivot;
CREATE VIEW my_pivot AS
SELECT c.customer_id
, c.created_at
, c.partner_id
, MAX(CASE WHEN fact_name = 'name' THEN fact_value END) name
, MAX(CASE WHEN fact_name = 'surname' THEN fact_value END) surname
, MAX(CASE WHEN fact_name = 'city' THEN fact_value END) city
FROM customers c
LEFT
JOIN customers_facts f
ON f.customer_id = c.customer_id
GROUP
BY c.customer_id;
We can interrogate this VIEW with a simple query - e.g. SELECT customer_id FROM my_pivot WHERE name = 'Milton', however, this cannot use an index, so it's not very efficient.
Also, because of the way in which we created the VIEW, it cannot be updated...
UPDATE my_pivot SET name = 'Leonardo' WHERE customer_id = 1;
ERROR 1288 (HY000): The target table my_pivot of the UPDATE is not updatable
However, had we created the VIEW slightly differently, then it could be updated...
DROP VIEW IF EXISTS my_pivot;
CREATE VIEW my_pivot AS
SELECT c.customer_id
, c.created_at
, c.partner_id
, name.fact_value name
, surname.fact_value surname
, city.fact_value city
FROM customers c
LEFT
JOIN customers_facts name
ON name.customer_id = c.customer_id
AND name.fact_name = 'name'
LEFT
JOIN customers_facts surname
ON surname.customer_id = c.customer_id
AND surname.fact_name = 'surname'
LEFT
JOIN customers_facts city
ON city.customer_id = c.customer_id
AND city.fact_name = 'city';
UPDATE my_pivot SET name = 'Leonardo' WHERE customer_id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
SELECT * FROM customers_facts;
+-------------+-----------+------------+
| customer_id | fact_name | fact_value |
+-------------+-----------+------------+
| 1 | city | Milan |
| 1 | name | Leonardo |
| 2 | name | Orlando |
| 2 | surname | Bloom |
+-------------+-----------+------------+
...but this still cannot use an index.
EDIT: To answer the question asked in comments below your question, you can do...
SELECT customer_id
FROM customers_facts
WHERE
( fact_name,fact_value ) IN (('name','Orlando'),('surname','Bloom'))
GROUP
BY customer_id
HAVING COUNT(*) = 2;
...although I think MySQL can't use an index in this instance, so the longhand version might be better...
SELECT customer_id
FROM customers_facts
WHERE
( fact_name = 'name'
AND fact_value = 'Orlando'
)
OR
( fact_name = 'surname'
AND fact_value = 'Bloom'
)
GROUP
BY customer_id HAVING COUNT(*) = 2;
I am working on sql query in mysql for example I am inserting records like key and values pair
Student Table
StdId StuName phnNum
1 John 87678
I am storing detailed Info about that student in student_metadata table
S.NO FieldName FieldValue indexVal
1 college St.Anns 1
2 Address Arizona 1
3 IdNum 321 1
Now I want to fetch student_metadata
SELECT
student.stdId AS StdId
fieldValue AS fieldValue
FROM
Student
LEFT JOIN
Student_metadata ON Student.StdId = student_metadata.indexVal
Where
Student_metadata.fieldName IN ('College','IdNum')
Here my problem is If I use 'IN' it gives me 2 rows. like the below
StudentId fieldValue
1 St.Anns
1 321
I want to fetch like the below
StudentId fieldValue IdNum
1 St.Anns 321
Can any one suggest me in this
You can use conditional aggregation to pivot:
select
s.stdid,
max(case when m.fieldname = 'College' then fieldvalue end) as fieldvalue,
max(case when m.fieldname = 'IdNum' then fieldvalue end) as idnum
from student s
left join student_metadata m on s.stdid = m.indexval
where m.fieldname in ('College','IdNum')
group by s.stdid
You could use a pivot table style query:
SELECT
a.`StuName`,
COALESCE(b.`FieldVal`) as `college`,
COALESCE(c.`FieldVal`) as `IdNum`
FROM `Student` a
LEFT JOIN `student_metadata` b
ON b.`indexVal` = a.`StdId` AND b.`FieldName` = 'college'
LEFT JOIN `student_metadata` c
ON c.`indexVal` = a.`StdId` AND c.`FieldName` = 'IdNum'
WHERE a.`StdId` = 1
GROUP BY a.`StdId`
Is there a way to select rpd.name using a different WHERE clause if the previous one returns an empty string?
I've looked into CASE, but I'm not sure how would I go about using it.
table replacement_part
part_id href
1 url_1
2 url_2
3 url_3
table replacement_part_description
part_id language_id name
1 2
1 1 hose
2 2
2 1 control module
3 2 vonkajsi kryt
3 1 outer casing
expected output
part_id href name
1 url_1 hose
2 url_2 control module
3 url_3 vonkajsi kryt
SELECT *
FROM replacement_part AS rp
LEFT JOIN replacement_part_description AS rpd
ON (rp.part_id = rpd.part_id)
WHERE language_id = :id
So something like
if rpd.name = ''
WHERE language_id = a,
else
WHERE language_id = b
This ?
SELECT *
FROM replacement_part AS rp
LEFT JOIN replacement_part_description AS rpd
ON (rp.part_id = rpd.part_id)
WHERE
(
language_id = :id
AND (rpd.name = '' OR rpd.name IS NULL)
)
OR language_id = b
One way is to join the rpd table twice, moving the where clause on the ON condition, and then use COALESCE:
SELECT
...,
COALESCE(rpd1.name, rpd2.name) AS name
FROM
replacement_part rp LEFT JOIN replacement_part_description rpd1
ON rp.part_id = rpd1.part_id AND rpd1.language=a
LEFT JOIN replacement_part_description rpd2
ON rp.part_id = prd2.part_id AND rpd2.language=b
here I suppose that the language is on the description table. If name is an empty string, instead of coalesce you could use CASE WHEN:
CASE WHEN rpd1.name='' OR rpd1.name IS NULL THEN rpd2.name ELSE rpd1.name END AS Name
You can use order by and limit to get one row with priorities:
select . . .
from . . .
where (language_id = a and rpd.name = '') or
language_id = b
order by (language_id = a) desc -- put the "a" rows first
limit 1;
Why is my Coalesce() statement in my initial Select statement not replacing the Null with a 0?
Select * From
(
Select a.orderstatus As [Stat], Coalesce(Count(b.id), '0') As [Count], b.region
From orderstatus a
Left Join saleinfo b
on b.orderstatus = a.orderstatus
Group By a.orderstatus, b.region
) one
pivot ( Max([Count]) For region In ([East], [West], [North], [South]) ) pv
Because you are using it in the inner query, whereas the problem is that the record doesn't it exist so the PIVOT is creating the NULL after the inner query has been processed. If your query (one) returned:
Stat Count Region
-------------------------
Stat1 0 East
Stat2 2 East
Stat1 5 West
You will end up with a Pivot Table like
Stat East West North South
---------------------------------------
Stat1 0 5 NULL NULL
Stat2 2 NULL NULL NULL
For example you get NULL for (Stat2, West) because there is no result in your subquery, so the COALESCE does not help. Your work around would be to just use COUNT in the PIVOT itself:
SELECT pvt.OrderStatus, pvt.East, pvt.West, pvt.North, pvt.South
FROM ( SELECT os.OrderStatus, si.Region, si.ID
FROM OrderStatus AS os
LEFT JOIN SaleInfo AS si
ON si.OrderStatus = b.OrderStatus
) AS t
PIVOT
( COUNT(ID)
FOR Region IN ([East], [West], [North], [South])
) AS pvt;
Or to put the COALESCE in the outer select:
SELECT pvt.OrderStatus,
East = COALESCE(pvt.East, 0),
West = COALESCE(pvt.West, 0),
North = COALESCE(pvt.North, 0),
South = COALESCE(pvt.South, 0)
FROM ( SELECT os.OrderStatus, si.Region, [Count] = COUNT(si.ID)
FROM OrderStatus AS os
LEFT JOIN SaleInfo AS si
ON si.OrderStatus = b.OrderStatus
) AS t
PIVOT
( MAX([Count])
FOR Region IN ([East], [West], [North], [South])
) AS pvt;
I much prefer the first option though.
EDIT
Example showing 0 returned for non existent data when using COUNT:
SELECT pvt.Stat, pvt.East, pvt.West, pvt.North, pvt.South
FROM (VALUES
('Stat1', 'East', 1),
('Stat2', 'East', 2),
('Stat1', 'West', 3)
) t (Stat, Region, ID)
PIVOT
( COUNT(ID)
FOR Region IN ([East], [West], [North], [South])
) AS pvt;
Please consider the following table structure and data:
+--------------------+-------------+
| venue_name | listed_by |
+--------------------+-------------+
| My Venue Name | 1 |
| Another Venue | 2 |
| My Venue Name | 5 |
+--------------------+-------------+
I am currently using MySQL's GROUP BY function to select only unique venue names. However, this only returns the first occurance of My Venue Name, but I would like to return it based on a condition (in this case where the listed_by field has a value > 2.
Essentially here's some pseudo-code of what I'd like to achieve:
Select all records
Group by name
if grouped, return the occurance with the higher value in listed_by
Is there an SQL statement that will allow this functionality?
Edit: I should have mentioned that there are other fields involved in the query, and the listed_by field needs to be used elsewhere in the query, too. Here is the original query that we're using:
SELECT l1.field_value AS venue_name,
base.ID AS listing_id,
base.user_ID AS user_id,
IF(base.user_ID > 1, 'b', 'a') AS flag,
COUNT(img.ID) AS img_num
FROM ( listingsDBElements l1, listingsDB base )
LEFT JOIN listingsImages img ON (base.ID = img.listing_id AND base.user_ID = img.user_id and img.active = 'yes')
WHERE l1.field_name = 'venue_name'
AND l1.field_value LIKE '%name%'
AND base.ID = l1.listing_id
AND base.user_ID = l1.user_id
AND base.ID = l1.listing_id
AND base.user_ID = l1.user_id
AND base.active = 'yes'
GROUP BY base.Title ORDER BY flag desc,img_num desc
As long as you didn't mention other fields - here is the simplest solution:
SELECT venue_name,
MAX(listed_by)
FROM tblname
WHERE listed_by > 2
GROUP BY venue_name
With other fields it could look like (assuming there is no duplicates in venue_name + listed_by pairs):
SELECT *
FROM tblname t1
INNER JOIN (SELECT venue_name,
MAX(listed_by) max_listed_by
FROM tblname
WHERE listed_by > 2
GROUP BY venue_name) t2 ON t1.venue_name = t2.venue_name
AND t1.listed_by = t2.max_listed_by