DBIx::Class select from a subquery - sql

I'm trying to write the following SQL query using DBIx::Class:
SELECT * FROM (SELECT username, DENSE_RANK () OVER (ORDER BY COUNT(username) DESC) FROM myTable GROUP BY username) AS topUsers WHERE username='myUsername';
myTable consists of only two rows, one being the username and the other one representing an achievement of the user. The subquery is supposed to assign a rank to each user and the main query selects the rank of myUser.
I'm having some issues in translating this to DBIx::Class so I would appreciate some help!
Thank you in advance!

You should define your query as view. See here
After that you can:
$schema->resultset( 'YourView' )->search
This will generate SQL:
select * from ( <your query here> )

Related

Ambiguous column name using row_number() without alias

I'm trying to implement pagination in a query that is built using information from a view, and I need to use the row_number() function over a column when I don't know which table it is from.
SELECT * FROM (
SELECT class.ID as ID, user.ID as USERID, row_number() over (ORDER BY
ID desc) as row_number FROM class, user
) out_q WHERE row_number > #startrow ORDER BY row_number
The problem is that I only have the result column name (ID or USERID) that came from a previous query. If I execute this query, it will raise the error 'Ambiguous column name "ID"'. Is there a way to specify that I'm referencing the column ID that is being selected and not from a different table?
Is it possible to specify an alias to the query result itself?
I have already tried the following,
SELECT TOP 30 * FROM (
SELECT *, row_number() over (ORDER BY ID desc) as row_number FROM(
SELECT class.ID as ID, user.ID as USERID FROM class, user
) in_q
) out_q WHERE row_number > #startrow ORDER BY row_number
It works, but the SGBD gets confused on which query plan it has to use, because of the small row goal present in the outer query and the big set of results returned by the inner query, when #startrow is a small number, the query executes in less than one second, when it is a big number the query takes minutes to execute.
Your problem is the id in the row_number itself. If you want a stable sort, then include both ids:
SELECT *
FROM (SELECT class.ID as ID, user.ID as USERID,
row_number() over (ORDER BY class.ID desc, user.id) as row_number
FROM class CROSS JOIN user
) out_q
WHERE row_number > #startrow
ORDER BY row_number;
I assume the cartesian product is intentional. Sometimes, this indicates an error in the query. In general, I would advise you to avoid using commas in the from clause. If you do want a cartesian product, then be explicit by using CROSS JOIN.
You could try using the option you already tried, then use the OPTIMIZE FOR hint.
OPTION ( OPTIMIZE FOR (#startrow = 100000) );
See a description of the hint in MSDN docs here: https://msdn.microsoft.com/en-us/library/ms181714.aspx.

SQL Server - Selecting multiple maximum values matching two separate corresponding cells

I am a beginner in SQL and been trying to obtain all of the maximum values as per the access strength and folder names, with allowing groups to the folders being displayed accordingly. I tried using MAX function but it was returning only 1 maximum result. Thank you in advance for any help or guidance of how t achieve the specified results.
Please see the jpg link attached for the top table and the desired outcome in the bottom table.
John
You can use the RANK function to put your records in an order, then select only the top 1:
WITH CTE AS
( SELECT Folder_name,
[User],
Access,
Group_Allowing,
[Rank] = RANK() OVER(PARTITION BY Folder_Name, [User] ORDER BY Access DESC)
FROM T
)
SELECT Folder_name,
[User],
Access,
Group_Allowing
FROM CTE
WHERE [Rank] = 1;
The PARTITION BY clause is similar to GROUP BY, and the ranking will start again at 0 for each group. The order by simply then indicates what order to do the ranking in.
You'll want to put your query using MAX() into a subquery and JOIN to it:
SELECT a.*
FROM YourTable a
JOIN (SELECT Folder_Name, [User], MAX(Access) Max_Access
FROM YourTable
GROUP BY Folder_Name, [User]
)b
ON a.Folder_Name = b.Folder_Name
AND a.[User] = b.[User]
AND a.Access = b.Max_Access
You can use "ORDER BY Access DESC" and "LIMIT [number of entries you want]" or if you want to just get the highest number you can use "GROUP BY Access" and then use max() to display all the entries you want

SQL Subquery - Inexperienced!

I have a database, and I'm trying to show the count of users for each different entry in the "source" field. So for example, show how many people share the same source. Would I have to do this through a subquery? Or do I have to actually know what the titles of the sources are in the database?
You can use GROUP BY:
SELECT COUNT(*), source FROM mytable GROUP BY source;
Sounds like you'd use grouping:
select source, count(*) from users group by source ;
SELECT Source, count(Source) as TotalPeople
FROM SomeTable
GROUP BY Source
You are looking for GROUP BY query.
Start here: http://www.sql-tutorial.com/sql-group-by-sql-tutorial/
This counts the record grouped by the values of myField:
SELECT COUNT(1) As rCount FROM myTable GROUP BY myField
You can also group your query with multiple GROUP clauses.
This will give you the counting of any sub-grouping:
SELECT myField1, myField2, COUNT(1) As rCount FROM myTable GROUP BY myField1, myField2
The COUNT(1) is better than COUNT(*), for the Sql engine.

SQL Max on Group By

I'm stuck at a very simple t-sql query. Please help !
Following is my result set :
Percentage---FirstName---SessionId
34-----------ABC---------222
67-----------ABC---------333
11-----------ABC---------444
83-----------XYZ---------555
23-----------XYZ---------666
64-----------XYZ---------777
From above, I want records of each user with maximum percentage e.g.
Percentage---FirstName---SessionId
67-----------ABC---------333
83-----------XYZ---------555
I can't seem to do that and I'm in hurry. Please help at the earliest. Any help would be greatly appreciated.
Thanks.
SELECT MAX(Percentage) AS Percentage,
FirstName
FROM mytbl
GROUP BY FirstName
Or if you need session id:
SELECT mytbl.*
FROM mytbl
INNER JOIN (SELECT MAX(Percentage) AS Percentage,
FirstName
FROM mytbl
GROUP BY FirstName) x ON x.Percentage = mytbl.Percentage
AND x.FirstName = mytbl.FirstName
If you need to deal with ties, then you might want to use the windowing functions (assuming you're on SQL Server 2005 or later):
SELECT
*
FROM
(SELECT mytbl.*,RANK() OVER (PARTITION BY FirstName ORDER BY Percentage desc) as rn) t
WHERE
t.rn = 1
So if there are two rows with the same percentage, they'll both be returned. If you only want one result, then you can add additional "tie-breaker" columns to the ORDER BY clause, or switch from RANK() to ROW_NUMBER().

SELECT *, COUNT(*) in SQLite

If i perform a standard query in SQLite:
SELECT * FROM my_table
I get all records in my table as expected. If i perform following query:
SELECT *, 1 FROM my_table
I get all records as expected with rightmost column holding '1' in all records. But if i perform the query:
SELECT *, COUNT(*) FROM my_table
I get only ONE row (with rightmost column is a correct count).
Why is such results? I'm not very good in SQL, maybe such behavior is expected? It seems very strange and unlogical to me :(.
SELECT *, COUNT(*) FROM my_table is not what you want, and it's not really valid SQL, you have to group by all the columns that's not an aggregate.
You'd want something like
SELECT somecolumn,someothercolumn, COUNT(*)
FROM my_table
GROUP BY somecolumn,someothercolumn
If you want to count the number of records in your table, simply run:
SELECT COUNT(*) FROM your_table;
count(*) is an aggregate function. Aggregate functions need to be grouped for a meaningful results. You can read: count columns group by
If what you want is the total number of records in the table appended to each row you can do something like
SELECT *
FROM my_table
CROSS JOIN (SELECT COUNT(*) AS COUNT_OF_RECS_IN_MY_TABLE
FROM MY_TABLE)