Custom Order By + Group By - mysql

So I have a manual in this table:
id lang header_name text
1 uk Youth Development It's very important
2 dk tst hejsa
3 uk tst hello sir
And I want to make a query that fetches all manual entries for a given language (danish in this case). If for some reason not all 100% of the original manual entries (the UK ones), has been translated I want to get the english entry instead. Is that even possible in table formats such as this?
I guess it would be something with a "group by header_name" of some sorts, but not sure.

Try this, i dont have an SQL and hence this is not tested
The tables t1, t2, t3 refer to the same table use an alias to distinguish them;
select * from t3
where t3.lang IN ('DK','UK')
and t3.ID NOT IN
(select t1.id
FROM t1,t2
where t1.header_name = t2.header_name
AND t2.lang = 'DK'
AND t1.lang = 'UK'
)
Essentially first you need to find the ID that have translation, and then exclude them.

This might do the trick but it is not optimized:
SELECT *
FROM the_table
WHERE lang = 'dk'
UNION
SELECT *
FROM the_table
WHERE lang <> 'dk' AND header_name NOT IN (
SELECT header_name
FROM the_table
WHERE lang = 'dk'
)

I cant comment but all i want to ask is:
In the example you just put up, the rows with Id 2 and ID 3 are the same entries only different language?
Id say you pull it out and make two tables
Example
id
sort
(all other generic columns)
example_translations
id
example_id
language_id
header_name
text
Then if querying for the danish translation of an example with id 1 it'll return the example_translations row of this entity. if it returns nothing you can query for the english version.
I dont think it is possible to do something like this on Mysql level

The way i understand this, you want to get the english content if the danish content is missing?.. You might want to add a column to your table where you mark your entries. (dont know if your "header_name" column does that efectly for you, i'm guessing that as well will be translated?..
Anyway, a column named "entry_id" where "tst dk" and "tst uk" would both have id "2" for an example, you should then when you load you manual ask for the "entry_id" and first look for the dk entry, and if it's not there, load the uk entry.

Related

MySQL join with conditional "on" statement

Say I have the below 2 tables:
text
id
text_lang
id
media_id
lang
text
(these are not the actual table structures - but I just have 2 tables with one containing all the localized strings that "belong" to the first table)
I want to join the tables, with a "preferred" language. So basically:
SELECT text.id, text_lang.text
FROM text
LEFT JOIN text_lang ON text.id = text_lang.text_id
AND (only a single row, and if there's a row with lang="en", pick that,
otherwise just pick any language)
The part I'm having trouble with is the "only a single row, and if there's a row with lang="en" pick that, otherwise just pick any language".
I think this does what I'm looking for:
SELECT text.id, text_lang.text
FROM text
JOIN text_lang ON text_lang.text_id = text.id AND
text_lang.lang = (select lang
FROM text_lang sub_text_lang
WHERE sub_text_lang.text_id = text.id
ORDER BY text_lang.lang = "en" DESC
limit 1)
... but I'd like to avoid a sub-query if possible, as I assume that would be quite a hit to the query performance.
I can think of a few ways but since you're trying to avoid subquery, perhaps you can do something like this:
SELECT t.id,
SUBSTRING_INDEX(
GROUP_CONCAT(text ORDER BY CASE WHEN lang='en' THEN 0 ELSE RAND() END),',',1)
AS extracted_text
FROM
text t
LEFT JOIN text_lang tl ON tl.text_id=t.id
GROUP BY t.id;
Use GROUP_CONCAT() with a customized ORDER BY then use SUBTRING_INDEX() to get the first value using comma (the default GROUP_CONCAT() separator) as the delimiter.
I've made a fiddle with sample data here: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=0d0501f36af1ba4e181856f761dbd5f7 . You can run the fiddle a few times to see that the text value other than 'en' change.

Can I query a record with multiple associated records that fit certain criteria?

I have two tables, Show, Character. Each Show has_many Characters.
class Show < ActiveRecord::Base
has_many :characters
class Character < ActiveRecord::Base
belongs_to :show
What I want to do is return the results of a Show that is associated with multiple Characters that fit certain criteria.
For example, I want to be able to return a list of Shows that have as characters both Batman and Robin. Not Batman OR Robin, Batman AND Robin.
So the query should be something like
Show.includes(:characters).where(characters: {'name = ? AND name = ?', "Batman", "Robin"})
But this returns an error. Is there a syntax for this that would work?
UPDATE
The query
Show.includes(:characters).where('characters.name = ? AND characters.name = ?', "Batman", "Robin")
returns a value of 0, even though there are definitely Shows associated with both Batman and Robin.
Using plain SQL, one solution is :
select s. *
from shows as s
join characters as c1 on (s.id=c1.show_id)
join characters as c2 on (s.id=c2.show_id)
where c1.name='Batman'
and c2.name='Robin';
Using Arel, I would translate as :
Show.joins('join characters as c1 on shows.id=c1.show_id').joins('join
characters as c2 on shows.id=c2.show_id').where('c1.name = "Batman"').where(
'c2.name="Robin"')
So you'll have to get a little fancy with SQL here; especially if you want it to be performant and handle different types of matchers.
select count(distinct(characters.name)) as matching_characters_count, shows.* from shows
inner join characters on characters.show_id = shows.id and (characters.name = 'Batman' or characters.name = 'Robin')
group by shows.id
having matching_characters_count = 2
To translate into ActiveRecord
Show.select('count(distinct(characters.name)) as matching_characters_count, shows.*').
joins('inner join characters on characters.show_id = shows.id and (characters.name = "Batman" or characters.name = "Robin")').
group('shows.id').
having('matching_characters_count = 2')
Then you'd probably do a pass with interpolation and then AREL it up; so you wouldn't be building string queries.
A plain SQL solution using aggregate functions. The SQL statements returns the ID values of the ´shows´ records you are looking for.
SELECT c.show_id
FROM characters c
WHERE c.name IN ('Batman','Robin')
GROUP BY c.show_id
HAVING COUNT(DISTINCT c.name) = 2
You can put this statement into a select_values() call and then grab the shows with the values of the returned array.
I think this must work:
super_heroes = ['Batman', 'Robin']
Show.where(
id: Character.select(:show_id).where(name: super_heroes).group(:show_id)
.having("count(distinct characters.name) = #{super_heroes.size}")
)
I just write the #sschmeck query in a Rails way, as a subquery, and add a super_heroes var to show how it can be scaled.
Reduce the number of entries in the query as soon as possible is basic idea to get a better query in performance.
Show.where("id IN (( SELECT show_id FROM characters WHERE name = 'Batman') INTERSECT (SELECT show_id FROM characters WHERE name = 'Robin'))")
In above query, the 1st sub query gets the 'show_id' for 'Batman', the next one gets the 'show_id' for 'Robin'. In my opinion, just a few characters match the condition after doing 'INTERSECT'. Btw, you can use explain command in 'rails db' for choosing what is better solution.
I think you're looking for this:
Show.includes(:characters).where(characters: {name: ["Batman", "Robin"]})
or
Show.includes(:characters).where('characters.name IN ?', ["Batman", "Robin"]).references(:characters)
The references is required because we're using a string in the where method.
So first of all, Why is your approach is wrong?
So in the "characters_shows" table you can find records that looks like this
show_id name character_id character_name
1 first show 1 batman
2 second 1 batman
1 first show 2 robin
1 first show 3 superman
As you can see there will never be a case where the character name is batman and robin at the same row
Another approach will be something like this
names = ["Batman", "Robin"]
Show.joins(:characters).where(characters: {name: names}).group("shows.id").having("COUNT(*) = #{names.length}")

Trouble with MySQL query

OK, first-off I admit MySQL Syntax has never been my strongest point. So, here I am.
Urls :
ID Url Code
===============================================
1 http://www.google.com Abcd
2 http://www.freetemplates4u.com Efgh
3 ...
Posts :
ID Title Address
===============================================
1 Some Title 1 http://mize.it/Abcd
2 Some Title 2 http://mize.it/Efgh
3 ...
I want to create a query to fetch the following table
Title Url
=======================================================
Some Title 1 http://www.google.com
Some Title 2 http://www.freetemplates4u.com
In a few words :
Take the Url-Code pairs from Urls table
Search for http://mize.it/+Code in the Posts table (in the Address field)
Combine the final Title and Url in a result table.
I know it has something to do with joins and concatenation, but I'm definitely lost.
SIDENOTE : I don't care neither about my current database's structure, nor about performance issues. All I want is to transfer existing data, from the existing database (without having to alter it), to my new website's database (under a totally different format/structure).
You should change your DB-Design, this query will have a poor performance since mysql has to do a full tablescan.
Try adding a code column in your Posts table hat has the right value (populate it on insert/update) and add an index to Code (both tables).
Now you should be able to do.
SELECT Posts.Title, Urls.Url
FROM Posts
INNER JOIN Urls ON Post.Code = Urls.Code
Update:
If the first part of the url is always the same, this will work
SELECT Post.Title, Urls.Url
FROM Posts
INNER JOIN Urls ON Post.Adress = CONCAT('http://mize.it/', Urls.Code)
TRY
SELECT p.title,x.url
FROM Posts p
INNER JOIN ( SELECT url, CONCAT('http://mize.it/',code) AS xcode FROM Urls ) x
ON (x.xcode = p.address)
Working DEMO
This is a different approch, it took a while for me to test it.
Since your Address field contains complete url and we only need to match what is after / so we can replace actual url with nothing (I assume url is always the same) and have string ready to be matched with Code field.
SELECT b.Title, a.URL
FROM Url a
LEFT JOIN Posts b
ON a.Code = REPLACE(b.Address, 'http://mize.it/', '')
ORDER BY a.ID ASC
Check following query.
select m1.Url, m2.Title from Urls as m1, Posts as m2
where m2.address like 'http://mize.it/%'

Query Multiple Rows in the same Table for one Variable

I have a table setup like this:
session_id | event_id | moderator | speaker_1 | speaker_2 | speaker_3 | ...keeps going to speaker_10
What I am trying to do is setup a query that searches for 1 variable "speakerid = 13245" and check rows
'moderator', 'speaker_1', 'speaker_2', 'speaker_3', 'speaker_4', 'speaker_5', 'speaker_6', 'speaker_7', 'speaker_8', 'speaker_9', 'speaker_10'
Then return every 'session_id' corresponding to any row that contains speakerid = 12345 in any of the 11 speaker rows.
I know it has something to do with an INNER JOIN but after a lot of searching I can't find anything specific enough. I've been following stackoverflow for years now and this is my first ever post.
It really sounds like you need to normalize this table and have a table of sessions/events and a table of speakers related to it through a third sesssions_speaker table. That way you don't need to change your table schema when you have an event with 12+ speakers.
That being said, you can query like this to get the result you need
SELECT session_id
FROM table
WHERE
moderator = ?
OR speaker_1 = ?
OR speaker_2 = ?
...
OR speaker_11 = ?
I think you just need to use LIKE with OR to return the rows where any field contains "speakerid = 12345":
SELECT Session_Id
FROM YourTable
WHERE Moderator Like '%speakerid = 13245%'
OR speaker_1 Like '%speakerid = 13245%'
OR ...
You should read up on database normalization as speaker_n columns are a bad sign. You probably want a Speakers table amd a "Session-Speakers" mapping table. This would certainly make your query easier, but for now you have no choice but to search all columns:
SELECT sesion_id FROM t1 WHERE
moderator = '12345'
OR speaker_1 = 12345
etc.
You can do this using in in the where clause:
select session_id
from t
where 13245 in (moderator, speaker_1, speaker_2, speaker_3, speaker_4,
speaker_5, speaker_6, speaker_7, speaker_8, speaker_9,
speaker_10)

mySQL query - Use results again in same query

I actually don't have any code yet to provide..but I can give you the data I am trying to manipulate.
I am working with a set of tags/keywords. Keywords can be related to another via the 'related_id' column.
So my table looks like:
keyword_tbl:
keyword_id | keyword | related_id
For this example, lets imagine the table is populated with the following entries
Entry 1:
keyword_id : 1
keyword: Marathons
related_id: 0
Entry 2:
keyword_id : 2
keyword: Boston
related_id: 1
As you can see, this entry of Boston, is related to Marathons via the related_id
I am working on giving the user the ability to search. If they search for an individual term, thats easy and not the question. However, if they search for "Boston Marathon," I now am having difficulty with the query.
SELECT * FROM keyword WHERE keyword LIKE "%boston%" OR keyword LIKE "%marathon%"
After this initial query, i'd like to compare the results, which would be the 2 entries I detailed above.
Id like to return only the term that is related to the other. In this case, Boston is the 'lowest' common denominator, and thus, I'd like to return it.
Imagine: Marathons -> Boston
Can this be done in a single query?
Thanks!
I'm thinking something like this might do the trick:
SELECT
a.*
FROM keyword a
JOIN keyword b
ON (a.related_id = b.keyword_id)
WHERE (a.keyword LIKE "%boston%"
OR a.keyword LIKE "%marathon%")
AND (b.keyword LIKE "%boston%"
OR b.keyword LIKE "%marathon%")
The below query will give you the answer
Marathons -> Boston.
IF there is a keyword that does not have a relation it will be displayed as
IceCream ->
SELECT resultset1.keyword,'->',IF(resultset2.keyword IS NOT NULL,resultset2.keyword,'')
FROM
(SELECT * FROM keyword WHERE keyword LIKE "%boston%" OR keyword LIKE "%marathon%")
as resultset1
LEFT JOIN
(SELECT * FROM keyword WHERE keyword LIKE "%boston%" OR keyword LIKE "%marathon%")
as resultset2
on resultset1.keyword_id=resultset2.related_id;