getting NonUniqueDiscoveredSqlAliasException - mysql

the table structure is as follows
+---------------+----------+-------------------+-------------+---------+--------+-------------------------+----------------+----------------+-----------------------+----------------------+---------------------+-------------------+--------------+
| REDEMPTION_ID | CCID | MEMBERSHIP_NUMBER | POINTS_TYPE | PARTNER
| SCHEME | REDEMPTION_ORDER_STATUS | MEMBER_SEGMENT | PARTNER_POINTS |
MEMBERSHIP_FIRST_NAME | MEMBERSHIP_LAST_NAME | REDEMPTION_DATE |
OUTBOUND_FILENAME | PRODUCT_TYPE |
+---------------+----------+--------
-----------+-------------+---------+--------+-------------------------+----------------+----------------+-----------------------+----------------------+---------------------+-------------------+--------------+
| 1003740 | 21212103 | 1231237 | BASE | QANTAS
| Visa | ORDERED | LEGACY | 5000.000000 |
e | Name | 2017-10-23 10:26:51 |
NABQF05P.012 | CONSUMER |
| 1003741 | 21212103 |1231238 | BONUS | QANTAS | Visa | ORDERED | LEGACY | 2500.000000 | e | Name
| 2017-10-23 10:26:51 | NABQF05P.012 | CONSUMER |
I want to group the above rows based on the columns Membership_Number and POINTS_TYPE and the resulting row should be one row.
I am using the following query :
select * from ((
select * from NAB_REDEMPTION_DETAILS
where PARTNER='QANTAS' and REDEMPTION_ORDER_STATUS IN ('PLACED','RESEND') and POINTS_TYPE = 'BASE' group by MEMBERSHIP_NUMBER) a
left OUTER JOIN (
select * from NAB_REDEMPTION_DETAILS
where PARTNER='QANTAS' and REDEMPTION_ORDER_STATUS IN ('PLACED','RESEND') and POINTS_TYPE = 'BONUS' group by MEMBERSHIP_NUMBER) b on a.MEMBERSHIP_NUMBER=b.MEMBERSHIP_NUMBER) union (
select * from (
select * from NAB_REDEMPTION_DETAILS
where PARTNER='QANTAS' and REDEMPTION_ORDER_STATUS IN ('PLACED','RESEND') and POINTS_TYPE = 'BASE' group by MEMBERSHIP_NUMBER) c right OUTER JOIN (
select * from NAB_REDEMPTION_DETAILS
where PARTNER='QANTAS' and REDEMPTION_ORDER_STATUS IN ('PLACED','RESEND') and POINTS_TYPE = 'BONUS' group by MEMBERSHIP_NUMBER) d on c.MEMBERSHIP_NUMBER=d.MEMBERSHIP_NUMBER)
when executing the query I am getting the above mentioned exception

I figured it out. since I was using joins in the same table and the Redemption_id was a Primarykey, It was resulting in the exception. I used group_concat instead of join and it solved my problem.
Kindly find the solution in the link http://sqlfiddle.com/#!9/dc59a1/5

Related

Join/Merge Multiple Table with same column name

I want to join three tables respectively from the below SQLFiddle
http://sqlfiddle.com/#!9/5dd558/4
Now I want to create one table from this table based on date and Brand.
Like, I want data in this manner
Date, Brand, Series, Table_1_Viewers, Table_2_Viewers, Table_2_Viewers
and if data is not matched on the table then the field should be nulled.
What I have done
SELECT h.*,
a.`amazon_viewers` AS "Table_1_Viewers",
n.`views` AS "Table_2_Viewers",
FROM `Table-1` h
LEFT JOIN `Table-2` a
ON h.`date` = a.`date`
AND h.`brand` = a.`brand`
LEFT JOIN `Table-3` n
ON h.`date` = n.`date`
AND h.`brand` = n.`brand`
Obviously I am selecting data from table-1 so it will display brand column only from table-1 but how can I get all table's brand column name in one column and merge these tables.??
The output I want...
| Date | Brand | Series | Table_1_Viewers | Table_2_Viewers | Table_3_Viewers |
|:----------:|:--------:|:--------------:|:---------------:|:---------------:|:---------------:|
| 12/1/2018 | TEST | TEST_SERIES | 100 | | |
| 10/15/2018 | MTV | GOT | 1000 | | 1000 |
| 12/1/2018 | TEST | Viking | 485632 | 856325 | |
| 12/1/2018 | TEST | Another Series | | 200 | |
| 10/15/2018 | POGO | GOT | | 1000 | |
| 7/1/2019 | No_Match | TEST_SERIES | | | 100 |
| 12/1/2018 | TEST-5 | Viking | | | 953022 |
You can do union all with aggregation :
select t.Date, t.Brand, t.Series_name,
sum(case when table_view = 't1' then amazone_viewer else 0 end) as Table_1_Viewers,
. . .
from (select h.date, h.brand, h.series_name, h.amazone_viewer, 't1' as table_view
from `Table-1` h union all
select h1.date, h1.brand, h1.series, h1.viewes, 't2'
from `Table-2` h1 union all
. . .
) t
group by t.Date, t.Brand, t.Series_name;

MySQL: Multi Tables - One to many relationship, Get column whose one of child's column is not null

I need help in creating sql query.
I want to get all orders whose none of child's res_id is null.
In the below example you will see order_audit.order_id W1 have one to
many relationship temp_order_id W1_1 and W1_2. This temp_order_id has further res_id 12 and 32. This order W1 should be in response.
In case of W2 you can see W2_1 has resp_id null. So this should not be pulled.
order_audit
+----+----------+
| id | order_id |
+----+----------+
| 1 | W1 |
| 2 | W2 |
| 2 | W3 |
+----+----------+
order_mapping
+----------+---------------+
| order_id | temp_order_id |
+----------+---------------+
| W1 | W1_1 |
| W1 | W1_2 |
| W2 | W2_1 |
| W2 | W2_2 |
| W3 | W3_1 |
+----------+---------------+
temp_order_table
+---------------+--------+
| temp_order_id | res_id |
+---------------+--------+
| W1_1 | 12 |
| W1_2 | 32 |
| W2_1 | null |
| W2_2 | 33 |
| W3_1 | null |
+---------------+--------+
From you screenshot it looks like there is a leading space in Account (and maybe there are trailing ones as well).
Any kind of help would be appreciated
You can natural join all 2 other tables and check for if res_id is null.
select oa.id, oa.order_id from order_audit oa
where not exists (
select * from order_mapping om
join temp_order_table tot on
tot.temp_order_id = om.temp_order_id
where om.order_id = oa.order_id and tot.res_id is null
)
Here is the link for sqlfiddle link
You could use a NOT IN forn the order with null
select oa.order_id
from order_audit oa
where oa.order_id NOT IN (
select om.order_id
from order_mapping om
inner join (
select to.temp_order_id
from temp_order_table to
where to.res_id is null
) t on t.temp_order_id = om.temp_order_id
)
I would approach this using group by and having:
select om.order_id
from order_mapping om left join
temp_order_table tot
on om.temp_order_id = tot.temp_order_id
group by om.order_id
having count(tot.temp_order_id) = count(tot.res_id);

Getting no of products in all categories and parent categories contains a keyword

I am trying to fetch all the categories and their count (no of products in that category) of those products where keyword matches. The query I tried doesn't give me the correct result.
Also I want the parent categories till level 1 and their count as well.
e.g. I am trying with keyword watch, then category "watches" should be there with some count. Also the parent category "accessories" with the sum of its descendant categories count.
my table structures are:
tblProducts: There are 5 categories of a product, fldCategoryId1, fldCategoryId2, fldCategoryId3, fldCategoryId4 and fldCategoryId5. fldProductStatus should be 'A'
+-----------------------------+-------------------+
| Field | Type |
+-----------------------------+-------------------+
| fldUniqueId | bigint(20) |
| fldCategoryId1 | bigint(20) |
| fldCategoryId2 | bigint(20) |
| fldCategoryId3 | bigint(20) |
| fldCategoryId4 | bigint(20) |
| fldCategoryId5 | bigint(20) |
| fldProductStatus | enum('A','P','D') |
| fldForSearch | longtext |
+-----------------------------+-------------------+
tblCategory:
+------------------------------+-----------------------+
| Field | Type |
+------------------------------+-----------------------+
| fldCategoryId | bigint(20) |
| fldCategoryName | varchar(128) |
| fldCategoryParent | int(11) |
| fldCategoryLevel | enum('0','1','2','3') |
| fldCategoryActive | enum('Y','N') |
+------------------------------+-----------------------+
Search Query:
SELECT count( c.fldCategoryId ) AS cnt, c.fldCategoryLevel, c.fldCategoryParent, c.fldCategoryId, c.fldCategoryName, p.fldForSearch, c.fldCategoryParent
FROM tblCategory c, tblProducts p
WHERE (
c.fldCategoryId = p.fldCategoryId1
OR c.fldCategoryId = p.fldCategoryId2
OR c.fldCategoryId = p.fldCategoryId3
OR c.fldCategoryId = p.fldCategoryId4
OR c.fldCategoryId = p.fldCategoryId5
)
AND p.fldProductStatus = 'A'
AND (
MATCH ( p.fldForSearch )
AGAINST (
'+(watches watch)'
IN BOOLEAN MODE
)
)
GROUP BY c.fldCategoryId
Note: The table is in the InnoDB engine and have FULLTEXT search index on 'fldForSearch' column.
EDIT: sample data can be found in sqlfiddle
I'm not sure what you mean by:
Also I want the parent categories till level 1 and their count as well.
But the following query will show you a count for each category (including those with 0 found products), and a general rollup:
SELECT
c.fldCategoryId,
c.fldCategoryLevel,
c.fldCategoryName,
COUNT( * ) AS cnt
FROM tblCategory c
LEFT JOIN tblProducts p ON
(c.fldCategoryId = p.fldCategoryId1
OR c.fldCategoryId = p.fldCategoryId2
OR c.fldCategoryId = p.fldCategoryId3
OR c.fldCategoryId = p.fldCategoryId4
OR c.fldCategoryId = p.fldCategoryId5)
AND p.fldProductStatus = 'A'
AND MATCH ( p.fldForSearch )
AGAINST (
'+(watches watch)'
IN BOOLEAN MODE
)
GROUP BY
c.fldCategoryId
c.fldCategoryLevel,
c.fldCategoryName
WITH ROLLUP;
Notes:
you cannot select p.fldForSearch if you expect a count of all the products in the category. fldForSearch is on a per product basis, it defeats the grouping purpose
I left joined with products so it returns the categories with 0 products matching your keywords. If you don't want this to happen just remove the LEFT keyword
I haven't checked the MATCH condition I assume it's correct.
Start by not splaying an array (fldCategoryId...) across columns. Instead, add a new table.
Once you have done that, the queries change, such as getting rid of OR clauses.
Hopefully, any further issues will fall into place.
Since your category tree has a fixed height (4 levels), you can create a transitive closure table on the fly with
SELECT c1.fldCategoryId AS descendantId, c.fldCategoryId AS ancestorId
FROM tblcategory c1
LEFT JOIN tblcategory c2 ON c2.fldCategoryId = c1.fldCategoryParent
LEFT JOIN tblcategory c3 ON c3.fldCategoryId = c2.fldCategoryParent
JOIN tblcategory c ON c.fldCategoryId IN (
c1.fldCategoryId,
c1.fldCategoryParent,
c2.fldCategoryParent,
c3.fldCategoryParent
)
The result will look like
| descendantId | ancestorId |
|--------------|------------|
| 1 | 1 |
| 2 | 1 |
| 2 | 2 |
| ... | ... |
| 5 | 1 |
| 5 | 2 |
| 5 | 5 |
| ... | ... |
You can now use it in a subquery (derived table) to join it with products using descendantId and with categories using ancestorId. That means that a product from category X will be indirectly associated with all ancestors of X (as well as with X). For example: Category 5 is a child of 2 - and 2 is a child of 1. So all products from category 5 must be counted for categories 5, 2 and 1.
Final query:
SELECT c.*, coalesce(sub.cnt, 0) as cnt
FROM tblCategory c
LEFT JOIN (
SELECT tc.ancestorId, COUNT(DISTINCT p.fldUniqueId) AS cnt
FROM tblProducts p
JOIN (
SELECT c1.fldCategoryId AS descendantId, c.fldCategoryId AS ancestorId
FROM tblcategory c1
LEFT JOIN tblcategory c2 ON c2.fldCategoryId = c1.fldCategoryParent
LEFT JOIN tblcategory c3 ON c3.fldCategoryId = c2.fldCategoryParent
JOIN tblcategory c ON c.fldCategoryId IN (
c1.fldCategoryId,
c1.fldCategoryParent,
c2.fldCategoryParent,
c3.fldCategoryParent
)
) tc ON tc.descendantId IN (
p.fldCategoryId1,
p.fldCategoryId2,
p.fldCategoryId3,
p.fldCategoryId4,
p.fldCategoryId5
)
WHERE p.fldProductStatus = 'A'
AND MATCH ( p.fldForSearch )
AGAINST ( '+(watches watch)' IN BOOLEAN MODE )
GROUP BY tc.ancestorId
) sub ON c.fldCategoryId = sub.ancestorId
Result for your sample data (without level, since it seems to be wrong anyway):
| fldCategoryId | fldCategoryName | fldCategoryParent | fldCategoryActive | cnt |
|---------------|-----------------|-------------------|-------------------|-----|
| 1 | Men | 0 | Y | 5 |
| 2 | Accessories | 1 | Y | 5 |
| 3 | Men Watch | 1 | Y | 3 |
| 5 | Watch | 2 | Y | 5 |
| 6 | Clock | 2 | Y | 3 |
| 7 | Wrist watch | 1 | Y | 2 |
| 8 | Watch | 2 | Y | 4 |
| 9 | watch2 | 3 | Y | 2 |
| 10 | fastrack | 8 | Y | 3 |
| 11 | swish | 8 | Y | 2 |
| 12 | digital | 5 | Y | 2 |
| 13 | analog | 5 | Y | 2 |
| 14 | dual | 5 | Y | 1 |
Demos:
sqlfiddle
rextester
Note that the outer (left joined) subquery is logically not necessary. But from my experience MySQL doesn't perform well without it.
There are still ways for performance optimisation. One is to store the transitive closure table in an indexed temporary table. You can also persist it in a regular table, if categories do rarely change. You can also manage it with triggers.

MySQL Performance - LEFT JOIN / HAVING vs Sub Query

Which of the following queries style is better for performance?
Basically, I'm returning many related records into one row with GROUP_CONCAT and I need to filter by another join on the GROUP_CONCAT value, and I will need to add many more either joins/group_concats/havings or sub queries in order to filter by more related values. I saw that, officially, LEFT JOIN was faster, but I wonder if the GROUP_CONCAT and HAVING through that off.
(This is a very simplified example, the actual data has many more attributes and it's reading from a Drupal MySQL architecture)
Thanks!
Main Records
+----+-----------------+----------------+-----------+-----------+
| id | other_record_id | value | type | attribute |
+----+-----------------+----------------+-----------+-----------+
| 1 | 0 | Red Building | building | |
| 2 | 1 | ACME Plumbing | attribute | company |
| 3 | 1 | east_side | attribute | location |
| 4 | 0 | Green Building | building | |
| 5 | 4 | AJAX Heating | attribute | company |
| 6 | 4 | west_side | attribute | location |
| 7 | 0 | Blue Building | building | |
| 8 | 7 | ZZZ Mattresses | attribute | company |
| 9 | 7 | south_side | attribute | location |
+----+-----------------+----------------+-----------+-----------+
location_transaltions
+-------------+------------+
| location_id | value |
+-------------+------------+
| 1 | east_side |
| 2 | west_side |
| 3 | south_side |
+-------------+------------+
locations
+----+--------------------+
| id | name |
+----+--------------------+
| 1 | Arts District |
| 2 | Warehouse District |
| 3 | Suburb |
+----+--------------------+
Query #1
SELECT
a.id,
GROUP_CONCAT(
IF(b.attribute = 'company', b.value, NULL)
) AS company_value,
GROUP_CONCAT(
IF(b.attribute = 'location', b.value, NULL)
) AS location_value,
GROUP_CONCAT(
IF(b.attribute = 'location', lt.location_id, NULL)
) AS location_id
FROM
records a
LEFT JOIN records b ON b.other_record_id = a.id AND b.type = 'attribute'
LEFT JOIN location_translations lt ON lt.value = b.value
WHERE a.type = 'building'
GROUP BY a.id
HAVING location_id = 2
Query #2
SELECT temp.* FROM (
SELECT
a.id,
GROUP_CONCAT(
IF(b.attribute = 'company', b.value, NULL)
) AS company_value,
GROUP_CONCAT(
IF(b.attribute = 'location', b.value, NULL)
) AS location_value
FROM
records a
LEFT JOIN records b ON b.other_record_id = a.id AND b.type = 'attribute'
WHERE a.type = 'building'
GROUP BY a.id
) as temp
LEFT JOIN location_translations lt ON lt.value = temp.location_value
WHERE location_id = 2
Using JOIN is preferable in most cases, because it helps optimizer to understand which indexes he can to use. In your case, query #1 looks good enough.
Of course, it works only if tables has indexes. Check table records has indexes on id, other_record_id, value and type columns, table location_translations on value

Efficient assignment of percentile/rank in MYSQL

I have a couple of very large tables (over 400,000 rows) that look like the following:
+---------+--------+---------------+
| ID | M1 | M1_Percentile |
+---------+--------+---------------+
| 3684514 | 3.2997 | NULL |
| 3684515 | 3.0476 | NULL |
| 3684516 | 2.6499 | NULL |
| 3684517 | 0.3585 | NULL |
| 3684518 | 1.6919 | NULL |
| 3684519 | 2.8515 | NULL |
| 3684520 | 4.0728 | NULL |
| 3684521 | 4.0224 | NULL |
| 3684522 | 5.8207 | NULL |
| 3684523 | 6.8291 | NULL |
+---------+--------+---------------+...about 400,000 more
I need to assign each row in the M1_Percentile column a value that represents "the percent of rows with M1 values equal or lower to the current row's M1 value"
In other words, I need:
I implemented this sucessfully, but it is FAR FAR too slow. If anyone could create a more efficient version of the following code, I would really appreciate it!
UPDATE myTable AS X JOIN (
SELECT
s1.ID, COUNT(s2.ID)/ (SELECT COUNT(*) FROM myTable) * 100 AS percentile
FROM
myTable s1 JOIN myTable s2 on (s2.M1 <= s1.M1)
GROUP BY s1.ID
ORDER BY s1.ID) AS Z
ON (X.ID = Z.ID)
SET X.M1_Percentile = Z.percentile;
This is the (correct but slow) result from the above query if the number of rows is limited to the ones you see (10 rows):
+---------+--------+---------------+
| ID | M1 | M1_Percentile |
+---------+--------+---------------+
| 3684514 | 3.2997 | 60 |
| 3684515 | 3.0476 | 50 |
| 3684516 | 2.6499 | 30 |
| 3684517 | 0.3585 | 10 |
| 3684518 | 1.6919 | 20 |
| 3684519 | 2.8515 | 40 |
| 3684520 | 4.0728 | 80 |
| 3684521 | 4.0224 | 70 |
| 3684522 | 5.8207 | 90 |
| 3684523 | 6.8291 | 100 |
+---------+--------+---------------+
Producing the same results for the entire 400,000 rows takes magnitudes longer.
I cannot test this, but you could try something like:
update table t
set mi_percentile = (
select count(*)
from table t1
where M1 < t.M1 / (
select count(*)
from table));
UPDATE:
update test t
set m1_pc = (
(select count(*) from test t1 where t1.M1 < t.M1) * 100 /
( select count(*) from test));
This works in Oracle (the only database I have available). I do remember getting that error in MySQL. It is very annoying.
Fair warning: mysql isn't my native environment. However, after a little research, I think the following query should be workable:
UPDATE myTable AS X
JOIN (
SELECT X.ID, (
SELECT COUNT(*)
FROM myTable X1
WHERE (X.M1, X.id) >= (X1.M1, X1.id) as Rank)
FROM myTable as X
) AS RowRank
ON (X.ID = RowRank.ID)
CROSS JOIN (
SELECT COUNT(*) as TotalCount
FROM myTable
) AS TotalCount
SET X.M1_Percentile = RowRank.Rank / TotalCount.TotalCount;