MySQL Query to retrieve full URL slug - mysql

First question!
I have a MySQL table which stores all content on the various pages of a site. Let's say there's three fields, id(int), permalink(varchar) and parent(int)(. parent is the id of it's parent page.
I need a query that will build a full URL of a page, I'm guessing using CONCAT. I have it working fine for two levels, but can't figure out a way to make it scale for multiple levels; /root/level1/level2/ etc.
Here's what I have so far.
SELECT
CONCAT(
(SELECT permalink FROM content WHERE id = 2 LIMIT 1), # id = parent
"/",
(SELECT permalink FROM content WHERE id = 11 LIMIT 1)) as full_url
Any help, greatly appreciated!

That would be a recursive query, you have to use a stored procedure on the server (Which are avaiable in MySql #Claude).

You cannot do recursion in a query, you would have to use stored procedures but this is not available in MySQL.

Related

MySQL Stored Procedure with Parameters for Recursive CTE

I'm working on a MySQL Way of printing an "affiliate tree" and got the thing working with Common Table Expression. I'm using the following code right now:
WITH RECURSIVE recUsers AS
(
SELECT ID, username, sponsorID, 1 AS depth, username AS path
FROM users
WHERE id = 1
UNION ALL
SELECT c.ID, c.username, c.sponsorID, sc.depth + 1, CONCAT(sc.path, ' > ', c.username)
FROM recUsers AS sc
JOIN users AS c ON sc.ID = c.sponsorID
)
SELECT * FROM recUsers;
This selects the tree underneath the user with the id 1.
Now what I'd need to get is a way to pass that id as a parameter, so I don't need to define everything from the beginning every time I want to get the result.. So my idea is to put everything in a stored prodecure and pass the id in as a parameter.. However, so far I didn't get it working and always getting various errors that are very self speaking...
Basically what I've tried was
DELIMITER //
CREATE PROCEDURE getAffiliateTree(IN userid INT())
BEGIN
---my code here, the userid 1 replaced with userid
END//
DELIMITER;
However, this doesn't seem to work.. How can I get this done?
Two things I would suggest:
Use INT, not INT(). The optional length argument to integer types is deprecated in MySQL 8.0 (which I know you're using, because you're using CTE syntax). Even if you did use the length argument, using an empty argument is not legal syntax.
Make sure that the userid input parameter name is distinct from all of the columns in the tables you reference. That is, if the table has a column named userid (any capitalization), then change the name of your input parameter. Otherwise you may make ambiguous expressions like:
... WHERE userid = userid
Even though you intend one of these to be the column and the other to be the parameter, the SQL parser has no way of knowing that. It ends up treating both as the column name, so it's trivially true on all rows of the table.
Actually, a third thing I would suggest: when you ask questions, "it doesn't seem to work" isn't clear enough. Did it produce an error? If so, what was the full error message? Did it produce no error, but didn't give you the result you wanted? If so, show a mocked-up example of what you expected, and what the query produced that didn't match. It helps to be as clear as you can when you post questions, so readers don't have to guess what trouble you need help with.

Copying duplicates into new table - Old MySQL version complicates

Due to an older version of MySQL I'm having to use some pretty outdated methods to get things done.
At the moment I am trying to copy similar rows to another table based on a few distinct columns. The table holddups will be taking data from assets where the SKU and Description match those of one in holdkey. The command I'm running is:
INSERT INTO holddups
SELECT *
FROM assets, holdkey
WHERE assets.SKU = holdkey.SKU
AND assets.Description = holdkey.Description
And the error I'm getting is:
#1136 - Column count doesn't match value count at row 1
I hope this is enough to sort this all out, but if not feel free to ask more.
Selecting just * will take all columns from assets and holdkey and try to put it in holdups. But holdups does not have that much columns. Using assets.*will only take all columns of assets and that is what you want, right?
INSERT INTO holddups
SELECT assets.*
FROM assets, holdkey
WHERE assets.SKU = holdkey.SKU
AND assets.Description = holdkey.Description

Return first table data regardless if second table returns data, operator join normalization

I have two MySQL tables, 'sections' and 'pages'.
I'm trying to us a single query to fetch the data from one or both tables.
If the section and page exists the AND operator works fine.
If the section and page do not exist then the AND operator is fine as well.
However I can not seem to find a way to pull from the section table if the page does not exist (AND operator issue) in conjunction with index.php urls (pulls all the index.php pages for the site).
I'm trying to write better queries so here are my goals and what I have tried:
I do not want to pull all the pages that match the section and then iterate through them.
I do not want to waste resources on executing a second query if this can be done in a single query.
I currently have two if conditioned queries (only one will execute for a given page load) as pages with 'index.php' URL will pull for all sections.
I have used FROM in conjunction with the sections table though I have also tried LEFT JOIN on the page table as well.
This works for pages that aren't indexes:
(SELECT NULL AS meta_description FROM sections WHERE url = 'example')
UNION
(SELECT meta_description FROM pages WHERE url = 'something');
That query returns up to two rows and is easy enough to work with.
Section and page exists, returns result:
SELECT *
FROM sections AS cs
LEFT JOIN pages AS cp ON (cp.id_section = cs.id)
WHERE cs.section_url LIKE BINARY 'section_url' AND cp.url LIKE BINARY 'index.php'
Section exists, page does not, returns 0 rows...
SELECT *
FROM sections AS cs
LEFT JOIN pages AS cp ON (cp.id_section = cs.id)
WHERE cs.section_url LIKE BINARY 'section_url' AND cp.url LIKE BINARY 'not_exist'
Table structure:
pages: id, section_id, meta_description, meta_language, meta_robots, title, url
sections: id, section_id, meta_description, meta_language, meta_robots, title, url
Most importantly I want to ensure that the syntax is SQL neutral as I plan to eventually migrate from MySQL to PostgreSQL.
Mostly guessing, as I'm not sure what exactly you want to achieve, but try this:
SELECT *
FROM sections AS cs
LEFT JOIN pages AS cp ON (cp.id_section = cs.id AND cp.url LIKE BINARY 'not exists')
WHERE cs.section_url LIKE BINARY 'section_url'

Pivoting Concept

Hiii,
I have a Database design as:
Table File (FileID, Name, Details)
Table Attributes (AttID, AttName, AttType)
Table AttValues (FileID, AttID, AttValue)
Till the runtime, it is not known how many Attributes are in a File and of what names.
And I want to display after insertion at Frontend in a manner like like:
FileID, FileName, Details, (Rows of Attribute Table as Column here).
i.e.,
Can anybody provide me a piece of code in Java or in MySQL to achieve this Pivoting Result.
Highly thanks full for your precious time.
Or is there any other better way to store data, So that I can get the desired result easily.
This requires two queries. First select the File:
SELECT * FROM File WHERE (...)
Then, fetch the Attributes:
SELECT *
FROM AttValues
JOIN Attributes ON (Attributes.AttId = AttValues.AttId)
WHERE FileId = $id
The latter query will provide you with one row per Attribute, which you can programmatically pivot for display on your frontend:
foreach(row in result) {
table.AddColumn(Header = row['AttName'], Value = row['AttValue']);
}
Adapt to your local programming environment as needed.
Of course, this only works for a single File or Files with the same attributes. If you want to display multiple files with different Attributes you can instead prefetch all AttNames:
SELECT Attributes.AttId, Attributes.AttName
FROM Attributes
JOIN AttValues ON (Attributes.AttId = AttValues.AttId)
WHERE FileId IN ( $list_of_ids )
Then load the values like this:
SELECT *
FROM AttValues
WHERE FileId IN ( $list_of_ids )
and use a local associative array to map from AttIds to column indexes.
As a final optimisation, you can combine the last two queries into an OUTER JOIN to avoid the third round trip. While this will probably increase the amount of data transferred, it also makes filling the table easier, if your class library supports named columns.
I answered a similar question recently: How to pivot a MySQL entity-attribute-value schema. The answer is MySQL-specific, but I guess that's OK as the question is tagged with mysql.

Combine 'like' and 'in' in a SqlServer Reporting Services query?

The following doesn't work, but something like this is what I'm looking for.
select *
from Products
where Description like (#SearchedDescription + %)
SSRS uses the # operator in-front of a parameter to simulate an 'in', and I'm not finding a way to match up a string to a list of strings.
There are a few options on how to use a LIKE operator with a parameter.
OPTION 1
If you add the % to the parameter value, then you can customize how the LIKE filter will be processed. For instance, your query could be:
SELECT name
FROM master.dbo.sysobjects
WHERE name LIKE #ReportParameter1
For the data set to use the LIKE statement properly, then you could use a parameter value like sysa%. When I tested a sample report in SSRS 2008 using this code, I returned the following four tables:
sysallocunits
sysaudacts
sysasymkeys
sysaltfiles
OPTION 2
Another way to do this that doesn't require the user to add any '%' symbol is to generate a variable that has the code and exceute the variable.
DECLARE #DynamicSQL NVARCHAR(MAX)
SET #DynamicSQL =
'SELECT name, id, xtype
FROM dbo.sysobjects
WHERE name LIKE ''' + #ReportParameter1 + '%''
'
EXEC (#DynamicSQL)
This will give you finer controller over how the LIKE statement will be used. If you don't want users to inject any additional operators, then you can always add code to strip out non alpha-numeric characters before merging it into the final query.
OPTION 3
You can create a stored procedure that controls this functionality. I generally prefer to use stored procedures as data sources for SSRS and never allow dynamically generated SQL, but that's just a preference of mine. This helps with discoverability when performing dependency analysis checks and also allows you to ensure optimal query performance.
OPTION 4
Create a .NET code assembly that helps dynamically generate the SQL code. I think this is overkill and a poor choice at best, but it could work conceivably.
Have you tried to do:
select * from Products where Description like (#SearchedDescription + '%')
(Putting single quotes around the % sign?)
Dano, which version of SSRS are you using? If it's RS2000, the multi-parameter list is
not officially supported, but there is a workaround....
put like this:
select *
from tsStudent
where studentName like #SName+'%'
I know this is super old, but this came up in my search to solve the same problem, and I wound up using a solution not described here. I'm adding a new potential solution to help whomever else might follow.
As written, this solution only works in SQL Server 2016 and later, but can be adapted for older versions by writing a custom string_split UDF, and by using a subquery instead of a CTE.
First, map your #SearchedDescription into your Dataset as a single string using JOIN:
=JOIN(#SearchedDedscription, ",")
Then use STRING_SPLIT to map your "A,B,C,D" kind of string into a tabular structure.
;with
SearchTerms as (
select distinct
Value
from
string_split(#SearchedDescription, ',')
)
select distinct
*
from
Products
inner join SearchTerms on
Products.Description like SearchTerms.Value + '%'
If someone adds the same search term multiple times, this would duplicate rows in the result set. Similarly, a single product could match multiple search terms. I've added distinct to both the SearchTerms CTE and the main query to try to suppress this inappropriate row duplication.
If your query is more complex (including results from other joins) then this could become an increasingly big problem. Just be aware of it, it's the main drawback of this method.