Random elements inside JOIN - mysql

I have this code here
INSERT INTO Directory.CatalogTaxonomy (`CatalogId`, `TaxonomyId`, `TaxonomyTypeId`, `IsApprovalRelevant`)
SELECT cat.CatalogId, dep.Id, #department_type, false
FROM Directory.Catalog cat
JOIN (SELECT * FROM (
SELECT * FROM Taxonomy.Department LIMIT 10
) as dep_tmp ORDER BY RAND() LIMIT 3) AS dep
WHERE cat.CatalogId NOT IN (SELECT CatalogId FROM Directory.CatalogTaxonomy WHERE TaxonomyTypeId = #department_type)
AND cat.UrlStatus = #url_status_green
AND (cat.StatusId = #status_published
OR cat.StatusId = #status_review_required);
And the problem is that, it should for each catalog take the first 10 elements from Department and randomly choose 3 of them, then add to CatalogDepartment 3 rows, each containing the catalog id and a taxonomy id. But instead it randomly chooses 3 Department elements and then adds those 3 elements to each catalog.
The current result looks like this:
1 000de9d7-af8b-4bac-bdbd-e6e361e5bc5e
1 001d4060-2924-4c75-b304-d780454f261b
1 001bc4b8-c1bc-498d-9aee-3825a40587d5
2 000de9d7-af8b-4bac-bdbd-e6e361e5bc5e
2 001d4060-2924-4c75-b304-d780454f261b
2 001bc4b8-c1bc-498d-9aee-3825a40587d5
3 000de9d7-af8b-4bac-bdbd-e6e361e5bc5e
3 001d4060-2924-4c75-b304-d780454f261b
3 001bc4b8-c1bc-498d-9aee-3825a40587d5
As you can see, there are only 3 departments chosen and repeated for every catalog

If you think that the query:
SELECT * FROM (
SELECT * FROM Taxonomy.Department LIMIT 10
) as dep_tmp
ORDER BY RAND() LIMIT 3
that you join to Directory.Catalog returns 3 different departments for each catalog then you are wrong.
This query is executed only once and returns 3 random departments which are joined (always the same 3) to Directory.Catalog.
What you can do is after you CROSS JOIN 10 departments to Directory.Catalog, choose randomly 3 of them for each catalog.
Try this:
INSERT INTO Directory.CatalogTaxonomy (`CatalogId`, `TaxonomyId`, `TaxonomyTypeId`, `IsApprovalRelevant`)
WITH cte AS (
SELECT cat.CatalogId, dep.Id AS TaxonomyId, #department_type AS TaxonomyTypeId, false AS IsApprovalRelevant
FROM Directory.Catalog AS cat
CROSS JOIN (SELECT * FROM Taxonomy.Department LIMIT 10) AS dep
WHERE cat.CatalogId NOT IN (SELECT CatalogId FROM Directory.CatalogTaxonomy WHERE TaxonomyTypeId = department_type)
AND cat.UrlStatus = #url_status_green
AND (cat.StatusId = #status_published OR cat.StatusId = #status_review_required);
)
SELECT t.CatalogId, t.TaxonomyId, t.TaxonomyTypeId, t.IsApprovalRelevant
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY CatalogId ORDER BY RAND()) rn
FROM cte
) t
WHERE t.rn <= 3
Note that this:
SELECT * FROM Taxonomy.Department LIMIT 10
does not guarantee that you get the first 10 elements from Department because a table is not ordered.

Related

How to select N random groups within a table

My groups are split across rows like so:
Row Group
1 A
2 A
3 A
4 B
5 B
6 C
7 C
8 C
9 C
How can I select all rows for any 2 randomly chosen groups?
Sort random, pick 2 groups.
SELECT *
FROM your_table AS t
WHERE `Group` IN (
SELECT `Group` FROM (
SELECT `Group`
FROM your_table
GROUP BY `Group`
ORDER BY RAND()
LIMIT 2) q);
db<>fiddle here
To select rows of any two groups
select *
from t_groups
group by Group
having Group in ['A', 'B'];
To randomly select any two groups
select *
from t_groups
group by Group
having Group in (
SELECT Group FROM t_groups
ORDER BY RAND()
LIMIT 2);

SQL limiting the output to a number of results

I have this query.
SELECT * FROM Customer WHERE TAGID IN ('UK', 'Germany');
This outputs the results to show all customers in UK and Germany, but as you can imagine, there will be loads of outputs, and I just require it to output 2 results for UK and 2 results for Germany?
Is this possible?
UNION ALL comes to mind:
(SELECT c.*
FROM Customer c
WHERE TAGID = 'UK'
LIMIT 2
) UNION ALL
(SELECT c.*
FROM Customer c
WHERE TAGID = 'Germany'
LIMIT 2
);
Note that this can take advantage of an index onCustomer(TagID). Also, this returns two arbitrary rows. You can use an ORDER BY to return the newest, oldest, biggest, smallest, reddest, bluest or whatever.
Here is a db<>fiddle.
Yes It is possible. Please check below methods.
Method 1 :
SELECT TOP 2 * FROM Customer WHERE TAGID ='UK'
UNION ALL
SELECT TOP 2 * FROM Customer WHERE TAGID = 'Germany'
Method 2:
(SELECT * FROM Customer WHERE TAGID ='UK' LIMIT 2)
UNION ALL
(SELECT * FROM Customer WHERE TAGID = 'Germany' LIMIT 2)
Method 3:
(SELECT * FROM Customer WHERE TAGID ='UK' AND ROWNUM = 2)
UNION ALL
(SELECT * FROM Customer WHERE TAGID = 'Germany' AND ROWNUM = 2)

How to i order values depending on its numbers of hits

I want to retrieve the table value in order of from most to least. For example, if we had a single column table like the following,
selected_val
2
3
3
2
1
3
1
1
1
4
I need a SQL that will return the values in the order of 1, 3, 2, 4, because there are four 1's, three 3's, two 2's, and one 4 in the table. I want 1,1,1,1,3,3,3,2,2,4. Is this possible?
ANd if i add more colums that
selected_val Rack
2 A
3 A
3 B
2 B
1 C
3 C
1 A
1 A
1 B
4 C
Thanks for the help
you can use count and group by
select selected_val
from my_table
group by selected_val
order by count(*) DESC
for the secondo part of the question you can use group concat if you need the rack related to selected_vale
select selected_val, group_concat(rack)
from my_table
group by selected_val
order by count(*) DESC
or add the column rack to group by if you need the relative counting and order
select selected_val, rack
from my_table
group by selected_val, rack
order by count(*) DESC
TRY THIS for the desired output and you can select other columns as well:
select t.selected_val
from test t
inner join (
select t1.selected_val, count(1) ord
from test t1
group by t1.selected_val) t2 on t2.selected_val = t.selected_val
order by t2.ord desc
OUTPUT:
1
1
1
1
3
3
3
2
2
4
Output is not actually in row but you can use GROUP_CONCAT to retrieve output in a row.
Use subquery to count, then LEFT JOIN result to the subquery's result and order by count.
select t1.selected_val from table t1
left join (
SELECT
t2.selected_val,
count(*) AS count
FROM table t2
GROUP BY selected_val
) t3 on t1.selected_val = t3.selected_val
ORDER BY t3.count DESC;
Using Cross Join.
select A.C,A.Rack from
(select selected_val,Rack,count(*) c from #TableName group by selected_val,Rack)a
,(select selected_val,Rack,count(*) c from #TableName group by selected_val,Rack)b
where B.c <= A.selected_val
ORDER BY A.selected_val DESC
OutPut :

MySQL- Query with repetitive fixed id range

I have the following query:
SELECT child.id_catalog_category AS id_category, ancestor.id_catalog_category AS tree
FROM catalog_category AS child
JOIN catalog_category AS ancestor
ON (child.lft BETWEEN ancestor.lft AND ancestor.rgt)
WHERE ancestor.id_catalog_category != 1
ORDER BY id_category ASC, tree ASC
which is a reconstruction of a binary tree for a hierarchical product categories. For one id_category we could have a maximum of 4 "tree" values, as the example shows:
id_category / tree
3 2
3 3
4 2
4 3
4 4
5 2
5 3
5 5
6 2
6 3
6 6
7 2
7 3
7 7
Where the desired results should be :
id / id_category / tree
1 3 2
2 3 3
3 null null
4 null null
1 4 2
2 4 3
3 4 4
4 null null
.....
In words, I want to add a range id from 1 to 4 for each id_category, where if id_category has less than 4 value it should show null values.
Regards
To replicate the set of data you specified, I did this to get a working set:
CREATE TABLE cc (id_category INT, tree INT);
INSERT INTO cc VALUES (3,2),(3,3),(4,2),(4,3),(4,4),(5,2),(5,3),
(5,5),(6,2),(6,3),(6,6),(7,2),(7,3),(7,7);
SELECT cc.* FROM cc ORDER BY 1,2;
SQL Fiddle here: http://sqlfiddle.com/#!2/16249/3
Here's how I would approach the problem. First, I would get a distinct list of id_category values. That's straightforward. (I Could use a DISTINCT keyword rather than GROUP BY, whichever.)
SELECT id_category
FROM cc
GROUP BY id_category
ORDER BY id_category
Then, I would generate four rows from each of those rows. So, I'm going to wrap that previous query as an inline view (enclose it in a set of parenthesis, give it an alias, and reference the whole mess like it was just a tablename. Something like this:
SELECT c.id_category, j_.j
FROM (SELECT 1 AS j UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) j_
JOIN (
SELECT cc.id_category
FROM cc cc
GROUP BY cc.id_category
ORDER BY cc.id_category
) c
ORDER BY c.id_category, j_.j
I'm using another inline view to return the integers 1 thru 4, and doing a CROSS JOIN to get four rows for each distinct id_category. That basically gets me the outline of the result set I want to return... but I don't have any values (other than NULL) for the tree column.
So now, I need to backup, and start working on another rowset, basically an ordered set from the cc table, but this time, including the value from the tree column. I'm not concerned here with getting exactly four rows, just the rows that have a value in the tree column. Again, very straightforward:
SELECT s.id_category_id, s.tree
FROM cc s
ORDER BY s.id_category, s.tree
But now, I want to assign each of those rows a relative row number, within each id_category. I can do by wrapping that query in a set of parenthesis, giving it an alias, and treating it like it were a table, like this:
SELECT #i := IF(r.id_category = #prev_idcat,#i + 1,1) AS i
, #prev_idcat := r.id_category AS id_category
, r.tree
FROM (SELECT #i := 0, #prev_idcat := NULL) i_
JOIN (
SELECT s.id_category, s.tree
FROM cc s
ORDER BY s.id_category, s.tree
) r
I'm using a MySQL trick with user variables, to assign ascending integer values, starting at 1, for each distinct id_category. The trick here is to have MySQL order the rows for me (in the inline view aliased as r, and "saving" the id_category from the previous row in a user variable, so I can compare it to the next row.
And now we're really getting to the point where having Common Table Expressions available in MySQL would be really, really nice. But since they aren't, we press forward, nesting our inline views.
So I'm going to give each of those "row numbering" queries an alias, and reference them like they were tables; The query is going to be of the form...
SELECT b.*, q.*
FROM ( ) b
LEFT
JOIN ( ) q
ON q.id_category = b.id_category AND q.i = b.j
(We omit the contents of those inline views just to get an overview of what the statement is really doing.)
This is going to start looking ugly, but this is where the magic happens.
I pull the four rows for each id_category from b, and I join that to q, matching on id_category and on "row number". It's a LEFT OUTER join, so I'm going to get all the rows from b, and pick up any "matching" row from q.
SELECT b.id_category, q.tree
FROM (SELECT c.id_category, j_.j
FROM (SELECT 1 AS j UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
) j_
JOIN (
SELECT cc.id_category
FROM cc cc
GROUP BY cc.id_category
ORDER BY cc.id_category
) c
ORDER BY c.id_category, j_.j
) b
LEFT
JOIN (SELECT #i := IF(r.id_category = #prev_idcat,#i + 1,1) AS i
, #prev_idcat := r.id_category AS id_category
, r.tree
FROM (SELECT #i := 0, #prev_idcat := NULL) i_
JOIN (
SELECT s.id_category, s.tree
FROM cc s
ORDER BY s.id_category, s.tree
) r
) q
ON q.id_category = b.id_category AND q.i = b.j
ORDER BY b.id_category, b.j
The only thing remaining in the specification is the generation of a value for an id column. If I was inserting to a table, I could use an AUTO_INCREMENT column to do it for me. But absent that, the most convenient place for me to generate an id value is in the inline view aliased as b. Just a little tweak, and finally, we have this monstrosity of a query, which returns the specified result set:
SELECT b.k AS id, b.id_category, q.tree
FROM (SELECT #k := #k + 1 AS k
, c.id_category
, j_.j
FROM (SELECT #k := 0) k_
JOIN (SELECT 1 AS j UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
) j_
JOIN (
SELECT cc.id_category
FROM cc cc
GROUP BY cc.id_category
ORDER BY cc.id_category
) c
ORDER BY c.id_category, j_.j
) b
LEFT
JOIN (SELECT #i := IF(r.id_category = #prev_idcat,#i + 1,1) AS i
, #prev_idcat := r.id_category AS id_category
, r.tree
FROM (SELECT #i := 0, #prev_idcat := NULL) i_
JOIN (
SELECT s.id_category, s.tree
FROM cc s
ORDER BY s.id_category, s.tree
) r
) q
ON q.id_category = b.id_category AND q.i = b.j
ORDER BY b.id_category, b.j
To get this to work with your rowset, you would replace each reference to my cc table with your query wrapped in parenthesis. Or, you could create a table named cc like I have done, and insert the results of your query into it.
Someone may have a simpler SQL statement that reliably produces the same result set. I'd be very interested to learn a simpler way.

Mysql query random order (pause and continue) question

Okay i am trying to create a mysql query that does this:
show 3 random records from table then after the 3th record show TEXT
and then show the same 3 items but other field (equaling to the items ofcourse) from same table.
eg table info:
--ids | titles------
10 | one
20 | two
30 | three
and the query results from the given example:
30 10 20 TEXT three one two
if anyone understand what i am asking,post your suggestion/asnwer
thanks for your time all :)
Just for kicks..
select t1.id, t2.id, t3.id, 'TEXT', t1.title, t2.title, t3.title
FROM
(
select #r := #r + 1 rownum, id
from (select #r:=0) initvar, (
select id
from tbl
order by rand()
limit 3
) X
) Y
join tbl t1 on Y.rownum=1 and t1.id = Y.id
join tbl t2 on Y.rownum=2 and t2.id = Y.id
join tbl t3 on Y.rownum=3 and t3.id = Y.id
You should really just do the query below, and do whatever display processing using the 3 rows returned, in whatever programming environment you use (Java/PHP/.Net etc).
select id, title
from tbl
order by rand()
limit 3
EDIT
To get the data in 7 different rows, you can use the below. I stress again that this is front-end display code. I will not use such SQL code in a production system.
select display
from
(
select sorter, rownum,
case when sorter=3 then title else id end display
from
(
select #r := #r + 1 rownum, id, title
from (select #r:=0) initvar,
(
select id, title
from tbl
order by rand()
limit 3
) X
) Y, (select 1 sorter union all select 3) dup
union all
select 2, 0, 'TEXT'
) Z
order by sorter, rownum
Example Output
7
2
1
TEXT
test 7 << title for id=7
test 2
test 1