How to select top multiple of 10 entries? - sql

How to select top multiple of 10 entries?
I have a data in SQL table that is meaningful if only seen as bunch of 10 entries. I want to write a query that does this for ex. Select top 10*n from table where condition.
If for ex. 53 entries satisfy condition, I want only 50 to be seen and last 3 to be discarded.
Plz help.
Kbv

How about:
declare #rows int;
set #rows = ((select count(*) from table where condition)/10)*10
select top #rows * from table where condition

Try this:
with CTE AS (
SELECT * FROM Table WHERE Condition
)
Select top(((SELECT COUNT(*) FROM CTE)/10)*10) * From CTE

Please consider the following...
SELECT orderField,
field1,
...
FROM tblTable
WHERE condition
ORDER BY orderField
LIMIT 10 * numberOfGroups;
When constructing your query first decide which fields you want. In a simple one table query you can use SELECT * fairly safely, but if you are referring to a JOINed dataset then you should consider specifying which fields you are going to use and assigning aliases to those fields.
Make sure that whatever your orderField or orderFields are called they are covered by a wildcard such as * or by being explicitly specified.
The above code first selects all records that meet your criteria. It then sorts the resulting list based upon which field or fields you specify for ORDER BY. Note : The above assumes that you are sorting based upon existing values in your table. If you need to sort based on computed values then another (minor) level of complexity may need to be added.
LIMIT will then grab the first specified number of records from the sorted list. LIMIT accepts simply computed values such as 2 * 2 or 10 * numberOfGroups, where numberOfGroups is a variable set previously in the code or a value that explicitly replace numberOfGroups (i.e. 10 * #numberOfGroups where #numberOfGroups has previously been set to 5 or 10 * 5).
If you have any questions or comments, then please feel free to post a Comment accordingly.

Related

Transpose a single row multiple columns into multiple rows single colum

I have a table valued function in sql server which returns multiple rows and single column such as below
1
2
3
I use the syntax select * from dbo.function to use the values returned by this function in where clause of my queries.
Now apart from the value returned by the function I want to put certain hard coded values in that where clause.
For example :
Select * from dbo.table where ID in (Select * from dbo.function + **I want to add some more values here**)
So that if function returns
1
2
3
I want to add lets say
4
5
in that list such that final query becomes as follows :
select * from dbo.table where ID in (1,2,3,4,5)
Use or:
Select *
from dbo.table
where ID in (Select * from dbo.function) or
ID in (4, 5)
Although you could mangle the subquery using union all, the above makes the query easier to follow (in my opinion). Also, in the event that "function" is really a table, it is easier for the optimizer to recognize appropriate indexes.

Optional parameters in SQL query

I am new to SQL and I am kind of lost. I have a table that contains products, various fields like productname, category etc.
I want to have a query where I can say something like: select all products in some category that have a specific word in their productname. The complicating factor is that I only want to return a specific range of that subset. So I also want to say return me the 100 to 120 products that fall in that specification.
I googled and found this query:
WITH OrderedRecords AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY PRODUCTNUMMER) AS "RowNumber",
FROM (
SELECT *
FROM SHOP.dbo.PRODUCT
WHERE CATEGORY = 'ARDUINO'
and PRODUCTNAME LIKE '%yellow%'
)
)
SELECT * FROM OrderedRecords WHERE RowNumber BETWEEN 100 and 120
Go
The query works to an extent, however it assigns the row number before filtering so I won't get enough records and I don't know how I can handle it if there are no parameters. Ideally I want to be able to not give a category and search word and it will just list all products.
I have no idea how to achieve this though and any help is appreciated!
Building on what esiprogrammer showed in his answer on how to return only rows in a certain range using paging.
Your second question was:
Ideally I want to be able to not give a category and search word and it will just list all products.
You can either have two queries/stored procedures, one for the case where you do lookup with specific parameters, another for lookup without parameters.
Or, if you insist on keeping one query/stored procedure for all cases, there are two options:
Build a Dynamic SQL statement that only has the filters that are present; execute it using EXECUTE (#sql) or EXECUTE sp_executesql #sql
Build a Catch-All Query
Example for option 2:
-- if no category is given, it will be NULL
DECLARE #search_category VARCHAR(128);
-- if no name is given, it will be NULL
DECLARE #search_name VARCHAR(128);
SELECT *
FROM SHOP.dbo.PRODUCT
WHERE (#search_category IS NULL OR CATEGORY=#search_category) AND
(#search_name IS NULL OR PRODUCTNAAM LIKE '%'+#search_name+'%')
ORDER BY PRODUCTNUMMER
OFFSET 100 ROWS
FETCH NEXT 20 ROWS ONLY
OPTION(RECOMPILE); -- generate a new plan on each execution that is optimized for that execution’s set of parameters
If you just need to to paginate your query and return a specific range of results, you can simply use OFFSET FETCH Clause.
That way there is no need to filter result items by RowNumber. I think this solution is easier:
SELECT *
FROM SHOP.dbo.PRODUCT
WHERE CATEGORY = 'ARDUINO' AND PRODUCTNAAM LIKE '%yellow%'
ORDER BY PRODUCTNUMMER
OFFSET 100 ROWS -- start row
FETCH NEXT 20 ROWS ONLY -- page size
Find out more Pagination with OFFSET / FETCH
What do you mean it assigns the rownumber before filtering? Category and ProductName are part of the sub query... So if the product table has 10k records and only 1k meet your criteria the results from the CTE will be 1k, so RowNumber BETWEEN 100 and 120 works. Test it out, remove your where clauses from both select statement and you'll get rownumber for all of products table. Then add back in the category and productname filter and your RowNumber is for your filter ordered by ProductNumber, so when you then add back in Between 100 and 120, this is the right solution based on what you described.
WITH OrderedRecords AS
(
SELECT ROW_NUMBER() OVER (ORDER BY PRODUCTNUMMER) AS "RowNumber"
, *
FROM SHOP.dbo.PRODUCT
WHERE CATEGORY = 'ARDUINO'
and PRODUCTNAAM LIKE '%yellow%'
)
)
SELECT *
FROM OrderedRecords
WHERE RowNumber
BETWEEN 100 and 120
Go

Assign variable value within select, among other columns

I am aware that you can assign a value to a variable within a select by going
SELECT #blahBlah = (tbl.ProductGroup)
FROM tbl
Ok, now what I need to do is as follow.
Each account/user has a set number of transactions allowed every day; eg. Account 1 can do 5 transactions and Account 2 can do 3.
So in a select that will determine whether this account has overshot its limit and by how many, we have numerous inSelect calculations to produce column data.
The problem is that we now have about 4 columns that will each do a nested select to get the limits from another table, in order to produce a value for the current one.
What would be ideal is to assign these limits to a #variable in the select statement and to only reference that variable within said statement if needed.
To give a brief example (not actual logic):
SELECT
#Limit = (SELECT Limit From tClient Where tClient.clientId = tbl.ClientId),
(select count(*) from batch where batchItemCount > #limit) AS BadBatches,
....
FROM TBL
You get the picture.
Is there some method to pull this off?
Yes, you can use a Common Table Expression(CTE) to do these sort of things. The following is the basic idea that you can tweak to get it work for your case:
;WITH limtis AS
(
SELECT Limit, ClientId
From tClient
Where tClient.clientId = tbl.ClientId
)
select ...
from batch
INNER JOIN Limtis On ...
WHERE ...
Hope you got the picture.
Try something like this
select clientid from tClient as t1 inner join batch as t2
on t1.clientId=t2.clientId
group by clientId
having sum(batchItemCount)>sum(limit)

total number of rows of a query

I have a very large query that is supposed to return only the top 10 results:
select top 10 ProductId from .....
The problem is that I also want the total number of results that match the criteria without that 'top 10', but in the same time it's considered unaceptable to return all rows (we are talking of roughly 100 thousand results.
Is there a way to get the total number of rows affected by the previous query, either in it or afterwords without running it again?
PS: please no temp tables of 100 000 rows :))
dump the count in a variable and return that
declare #count int
select #count = count(*) from ..... --same where clause as your query
--now you add that to your query..of course it will be the same for every row..
select top 10 ProductId, #count as TotalCount from .....
Assuming that you're using an ORDER BY clause already (to properly define which the "TOP 10" results are), then you could add a call of ROW_NUMBER also, with the opposite sort order, and pick the highest value returned.
E.g., the following:
select top 10 *,ROW_NUMBER() OVER (order by id desc) from sysobjects order by ID
Has a final column with values 2001, 2000, 1999, etc, descending. And the following:
select COUNT(*) from sysobjects
Confirms that there are 2001 rows in sysobjects.
I suppose you could hack it with a union select
select top 10 ... from ... where ...
union
select count(*) from ... where ...
For you to get away with this type of hack you will need to add fake columns to the count query so it returns the same amount of columns as the main query. For example:
select top 10 id, first_name from people
union
select count(*), '' as first_name from people
I don't recommend using this solution. Using two separate queries is how it should be done
Generally speaking no - reasoning is as follows:
If(!) the query planner can make use of TOP 10 to return only 10 rows then RDBMS will not even know the exact number of rows that satisfy the full criteria, it just gets the TOP 10.
Therefore, when you want to find out count of all rows satisfying the criteria you are not running it the second time, but the first time.
Having said that proper indexes might make both queries execute pretty fast.
Edit
MySQL has SQL_CALC_FOUND_ROWS which returns the number of rows that query would return if there was no LIMIT applied - googling for an equivalent in MS SQL points to analytical SQL and CTE variant, see this forum (even though not sure that either would qualify as running it only once, but feel free to check - and let us know).

Assistance with SQL statement

I'm using sql-server 2005 and ASP.NET with C#.
I have Users table with
userId(int),
userGender(tinyint),
userAge(tinyint),
userCity(tinyint)
(simplified version of course)
I need to select always two fit to userID I pass to query users of opposite gender, in age range of -5 to +10 years and from the same city.
Important fact is it always must be two, so I created condition if ##rowcount<2 re-select without age and city filters.
Now the problem is that I sometimes have two returned result sets because I use first ##rowcount on a table. If I run the query.
Will it be a problem to use the DataReader object to read from always second result set? Is there any other way to check how many results were selected without performing select with results?
Can you simplify it by using SELECT TOP 2 ?
Update: I would perform both selects all the time, union the results, and then select from them based on an order (using SELECT TOP 2) as the union may have added more than two. Its important that this next select selects the rows in order of importance, ie it prefers rows from your first select.
Alternatively, have the reader logic read the next result-set if there is one and leave the SQL alone.
To avoid getting two separate result sets you can do your first SELECT into a table variable and then do your ##ROWCOUNT check. If >= 2 then just select from the table variable on its own otherwise select the results of the table variable UNION ALLed with the results of the second query.
Edit: There is a slight overhead to using table variables so you'd need to balance whether this was cheaper than Adam's suggestion just to perform the 'UNION' as a matter of routine by looking at the execution stats for both approaches
SET STATISTICS IO ON
Would something along the following lines be of use...
SELECT *
FROM (SELECT 1 AS prio, *
FROM my_table M1 JOIN my_table M2
WHERE M1.userID = supplied_user_id AND
M1.userGender <> M2.userGender AND
M1.userAge - 5 >= M2.userAge AND
M1.userAge + 15 <= M2.userAge AND
M1.userCity = M2.userCity
LIMIT TO 2 ROWS
UNION
SELECT 2 AS prio, *
FROM my_table M1 JOIN my_table M2
WHERE M1.userID = supplied_user_id AND
M1.userGender <> M2.userGender
LIMIT TO 2 ROWS)
ORDER BY prio
LIMIT TO 2 ROWS;
I haven't tried it as I have no SQL Server and there may be dialect issues.