Mixing results from 2 different MySQL queries for a user feed - javascript

I want to make a primitive personalized feed in a NodeJS+MySQL website. Now I just select all the posts that have specific tags in them:
SELECT column1, column2...
FROM table
WHERE tags = users_tags
ORDER BY relavance
LIMIT 8;
I want to also throw in a couple of popular posts eg:
SELECT column1, column2...
FROM table
ORDER BY relevance
LIMIT 2;
I don't want to use UNION because I want to retain the ordering from my first select and insert a popular result for every 5th post. Eg.: relevant, relevant, relevant, relevant, popular, relevant...
For now, I've results1.concat(results2) which adds them as the last two, and returned it. Then I had a for loop that would append the to HTML normally for the first 4 and then one from the back for every 5th.
Is there any better solution?

What you could use, is row_number to define the sortorder
SELECT column1,column2
FROM
(SELECT column1,column2,IF(rn = 1, B,D) as sortorder
FROM
(SELECT column1, column2, ROW_NUMBER() OVER(ORDER BY relavance) rn
FROM table1
ORDER BY relavance
LIMIT 2) t1
UNION
SELECT column1,column2,IF(rn < 5, A,C) as sortorder
FROM
(SELECT column1, column2, ROW_NUMBER() OVER(ORDER BY relavance) rn
FROM table1
WHERE tags = users_tags
ORDER BY relavance
LIMIT 8) t2) t3
ORDER BY sortorder

You can use ROW_NUMBER() window function to rank separately the posts with specific tags and the popular posts and then do a conditional sort:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY tags = users_tags ORDER BY relevance) rn
FROM tablename
)
SELECT *
FROM cte
ORDER BY CASE
WHEN tags = users_tags THEN rn + FLOOR((rn - 1) / 4)
ELSE (4 + 1) * rn
END
LIMIT 10;
Change 4 to the number of posts with specific tags that you want in each block.
See a simplified demo.

Related

How can I INSERT new values and values from an already existing table?

I'm currently learning MySQL and have learned the INSERTO INTO statement and the INSERT INTO SELECT statement.
The INSERT INTO statement works like this:
INSERT INTO table_name (column1, column2, column3, ...) VALUES (value1, value2, value3, ...);
While the INSERT INTO SELECT works like this:
INSERT INTO table2 (column1, column2, column3, ...)
SELECT column1, column2, column3, ...
FROM table1
WHERE condition;
What I'm trying to do is add new values while also adding values that I already have stored in another table.
con.query("INSERT INTO vehicles (vehicleType, vehicleModel, vehicleOwner, vehicleSpawnX, vehicleSpawnY, vehicleSpawnZ)")
From the query above, I already have the vehicleOwner value stored in another table, while the other ones I've just gotten.
How can I do this? Add a VALUES statement before SELECT?
I'm using SQL Workbench 8.0 and JavaScript. Also, all the values are NOT NULL, so I can't make two different queries, unless on the first one I add temporary values that I'll update on the second one.
What I want to replace:
vehicleType -> "players"
vehicleModel -> vehicleCreate.model
vehicleOwner -> playerID FROM players table
vehicleSpawnX -> pos.x
vehicleSpawnY -> pos.y
vehicleSpawnZ -> pos.z
Thanks!
It's not possible... But you can select data and store that on variables, then you will store it in another table
You would construct a query. Your data model is a bit hard to follow since you give no examples of the data or of what you really want to do.
So let me give a simpler example. Say, you have a table with two columns for two players and you want to put the ids into a table -- but using their names. The query would look like:
insert into pairs (playerId1, playerId2)
select p1.playerId, p2.playerId
from players p1 join
players p2
on p1.name = ? and p2.name = ?;
The ? are for parameter placeholders. I assume you know to aways use parameters when passing values into a query.

Query multiple not related tables in SQL

I have more than 50 tables that are not related but all of them have a "Name" field, I want to query "John" and get all Johns in the different tables and store each row in an array (Javascript) for example:
arr['table1']="results if any"
arr["table2"]="results if any".
What I'm doing right now is a for loop for each table:
SELECT * from tablesNameArray[i] WHERE name="John",
but I'm really wondering if there is any other better or "more" correct way to do it.
Thanks
You can do it in a single query using UNION:
SELECT * FROM table1 WHERE name = 'John'
UNION ALL
SELECT * FROM table2 WHERE name = 'John'
UNION ALL
SELECT * FROM table3 WHERE name = 'John'
...
You can construct the query dynamically from the array:
sql = tablesNameArray.map(table => `SELECT * FROM ${table} WHERE name = 'John'`).join(" SELECT ALL ");
You could use joins. See the join docs, MySQL in this case.
Something like this should do it:
SELECT table1.*, table2.*, table3.*
FROM table1
LEFT JOIN table2 ON table1.name = table2.name
LEFT JOIN table3 ON table1.name = table3.name
WHERE table1.name = "John";
A left join will still select from the first table even if the joined table doesn't have a matching row -- you'll get NULL in the second table's selected columns for those rows which didn't match.
However, depending on your requirements, this is possibly no good -- I believe it won't grab rows from table2 or table3 where there's no corresponding row for that user in table1.
As pointed out by #spencer7593 in a comment below, you should probably only do this if you're certain the name column is unique within each table, otherwise you could be generating some ridiculously huge result sets.

Sequalize limit by unique values in column

I have the following database structure:
id (int) | user_id (int) | product_id (int) | data (jsonb)
A combination of the id, user_id and product_id make the primary key. So there can be multiple rows with the same product_id and user_id.
The data column has some JSON containing the following
{ "item": boolean }
The query I need is to select all rows where user_id = x and data-->item = true. This part I can do, but I need to apply a limit. The limit should not restrict the number of rows that are returned, but instead restrict the number of DISTINCT product_ids that are returned. So if I apply a limit of 10 I could have 50 rows returned if each of the unique products have 5 rows belonging to the user_id and and item true.
This is what I have so far but it makes no attempt at this limit. I believe I may need a subquery or GROUPBY but I'm not sure how to achieve this in Sequalize.
return this.myModel.findAll({
where: {
user_id: userId,
'data.item': true,
},
});
Any guidance will be much appreciated! Thanks
A query to do this involves JOINing a subquery:
SELECT m.*
FROM my_model m
JOIN (
SELECT DISTINCT product_id FROM model LIMIT 10
) n ON n.product_id = m.product_id
WHERE m.user_id = $1 AND (data->>'item')::boolean IS TRUE;
To my knowledge, Sequelize cannot represent this query structure, although inlining the subquery as a literal may be possible. But it looks like you'll be running at least some raw SQL one way or the other.

How to get column_name and table_name from DML statement?

I am building an algorithm to mock DB operations.
I have one problem, how i can get Table name and column names from a DML statement?
i.e:
string = "SELECT id,name FROM USER_TBL"
string TABLE_NAME = getTableName(string); //this will return "USER_TBL"
array COLUMN_NAME = getColumnNames(string); //this will return ["id","name"]
If i consider these as string manipulation, how i can design algorithm to get table name & column names?
Currently i use following logic,
function getTableName(iString){
//find string between "FROM " to next " "(space) and return it
}
function getColumnNames(string){
//get string between "SELECT " and " FROM" and split string based on "," (comma) and return it
}
I wanted to know is there any algorithm already available for this(for reference)? What and all the cases i need to handle other than these?
I don't know an algorithm, but a few things that come to my mind:
Table name
- Tablenames can have aliases, but with your logic that should be OK
- Can be prefixed with the database name (dbname.tblname) -> so you might need to look for dots before returning the string
- Tablename can be a derived table --> select statement in brackets. In that case there is not really a simple to tablename derive
Column names
- Column names can have aliases, not sure how you want to handle that.
- What about calculations, conditions (Case when...) --> that is not just a column name but a combined or calculated value. In that case no real column name to retrieve
- You can have also select statements for single columns in the resultset
So overall, your approach works if there is no complexity in the sql queries, if yes, your logic might not return what you want to.
What about other statements, like UPDATE and INSERT? Is that also something that you want to parse?

is - select * from empty table possible?

On my website I am trying to select 2 tables - tableB of which might be empty, so its not returning any results at all when tableB is empty. I hope I am explaining this properly. Any suggestions?
curatio.webdb.getAllTodoItems = function(renderFunc) {
var db = curatio.webdb.db;
db.transaction(function(tx) {
tx.executeSql("SELECT * FROM tableA, tableB", [], renderFunc,
curatio.webdb.onError);
});
}
basically TableA has e.g. Name and Surname columns and TableB has e.g. Address details. But sometimes there is no address, and then I cannot get anything to display.
I need to basically ignore tableB if it's empty.
If tableA and tableB have the same schema, you can do something like this:
SELECT * FROM tableA
UNION
SELECT * FROM tableB
However, if they don't have the same schema, you will have to do something smarter to get the union to work.
Although your query doesn't hint at this, I still think you are probably looking for a LEFT JOIN, because I'm assuming you want to link data from two tables by a common value in a column from table B (known as the foreign key).
This query selects values of the first table, even when there's no matching data in the join table:
SELECT *
FROM `tableA` `a`
LEFT JOIN `tableB` `b`
ON `b`.`someReferenceColumnToTableA` = `a`.`theReferredColumn`
Again, this assumes you meant to join two tables by some common value, for instance:
person (table A):
- id
- name
phonenumber (table B):
- id
- person_id // this is the foreign key to table A that "links" the data
- phonenumber
If you were to use a regular JOIN (also known as INNER JOIN) then only rows are returned for when both tables have matching data.
What your original query did, however, was (implicitly) CROSS JOIN all data from both tables. Given your question edit, I hardly think this is what you were actually after.
What you are performing there is called a Cross-Product of those two tables. A cross-product is essentially every row of tableA with every row of tableB. Since tableB has no rows, the cross-product has no rows. If you want all rows of both tables, use 2 queries. I would recommend reading a basic SQL tutorial.
curatio.webdb.getAllTodoItems = function(renderFunc) {
var db = curatio.webdb.db;
db.transaction(function(tx) {
tx.executeSql("SELECT * FROM tableA UNION ALL SELECT * FROM tableB", [], renderFunc,
curatio.webdb.onError);
});
}
It will best to explicitly use the column names in both select rather then using the *.

Categories