sql query order by parts of - sql

lets say you have a table with 10 000 records of different email adresses, but within this tables there are a few hundred (this can vary and should not matter) addresses that contains a specific domain name ie #horses.com.
I would like in one single query retrieve all 10 000 record, but the ones that contains #horses.com will always be on top of the list.
Something like this " SELECT TOP 10000 * FROM dbo.Emails ORDER BY -- the records that contains #horses.com comes first"
OR
Give me 10000 records from the table dbo.Emails but make shure everyone that contains "#horses.com" comes first, no matter how many there is.
BTW This is on an sql 2012 server.
Anyone??

Try this:
SELECT TOP 10000 *
FROM dbo.Emails
ORDER BY IIF(Email LIKE '%#horses.com', 0, 1)
This assumes the email ends in '#horses.com', which isn't unreasonable. If you really want a contains-like function, add another % after the .com.
Edit: The IIF function is only available in sql server 2012 and later, for a more portable solution use CASE WHEN Email LIKE '%#horses.com' THEN 0 ELSE 1 END.

SELECT TOP 10000 *
FROM dbo.Emails
ORDER BY case when charindex('#horses.com', email) > 0
then 1
else 2
end,
email

SELECT 1,* FROM dbo.Emails where namn like '%#horses.com%'
union
SELECT 2,* FROM dbo.Emails where namn not like '%#horses.com%'
order by 1

Related

Is there possibility to assign 0 for another occurence of the same user?

I have table like this:
And lets say I want to have in another occurence of login eg. 1234 i would like to have instead of 275 value of 0, the same for 3678 instead of 300 i would like to have 0. I want to have profit/loss only for first occurence in the table. Is this somehow possible to do it in SQL?
If you only want to keep the highest / lowest, you can join the table with itself.
If you only want the first (as in lowest row number) occurance you can use a window function and update all occurances > 1
UPDATE SUB
set profit = 0
FROM (
SELECT
profit,
ROW_NUMBER() OVER (PARTITION BY Login ORDER BY Login) as cnt
FROM table
) SUB
WHERE SUB.cnt > 1
In Standard SQL, you can use:
update t
set profit = 0
where profit > (select max(profit) from t t2 where t2.login = t.login);
Note that specific databases may have alternative ways to writing this. However, your question does not have a database tag.
Also, this assumes that your table does not have duplicates. Unfortunately, duplicates would be problematic if these are the only two columns in the table.

How can I select the Nth row of a group of fields?

I have a very very small database that I am needing to return a field from a specific row.
My table looks like this (simplified)
Material_Reading Table
pointID Material_Name
123 WoodFloor
456 Carpet
789 Drywall
111 Drywall
222 Carpet
I need to be able to group these together and see the different kinds (WoodFloor, Carpet, and Drywall) and need to be able to select which one I want and have that returned. So my select statement would put the various different types in a list and then I could have a variable which would select one of the rows - 1, 2, 3 for example.
I hope that makes sense, this is somewhat a non-standard implementation because its a filemaker database unfortunately, so itstead of one big SQL statement doing all I need I will have several that will each select an individual row that I indicate.
What I have tried so far:
SELECT DISTINCT Material_Name FROM MATERIAL_READING WHERE Room_KF = $roomVariable
This works and returns a list of all my material names which are in the room indicated by the room variable. But I cant get a specific one by supplying a row number.
I have tried using LIMIT 1 OFFSET 1. Possibly not supported by Filemaker or I am doing it wrong, I tried it like this - it gives an error:
SELECT DISTINCT Material_Name FROM MATERIAL_READING WHERE _Room_KF = $roomVariable ORDER BY Material_Name LIMIT 1 OFFSET 1
I am able to use ORDER BY like this:
SELECT DISTINCT Material_Name FROM MATERIAL_READING WHERE Room_KF = $roomVariable ORDER BY Material_Name
In MSSQL
SELECT DISTINCT Material_Name
FROM MATERIAL_READING
WHERE _Room_KF = 'roomVariable'
ORDER BY Material_Name
OFFSET N ROWS
FETCH NEXT 5 ROWS ONLY
where N->from which row does to start
X->no.of rows to retrieve which were started from (N+1 row)

combine results from different selects

I have one table that contains a field "ID", "mailSent" and "serviceUsed". "mailSent" contains the time when a mail was sent and "serviceUsed" contains a counter that just says if the user has used the service for the particular mail that I have sent.
I am trying to do a report that gives me back for each ID the following two facts:
1. The last time when a user has used the service, i.e., the time when for a particular user serviceUsed != 0
2. The total number of times a user has used the service, i.e., sum(serviceUsed) for each user
I would like to display this in one view and map the result always to the particular user. I can build each of the two queries separately but do not know how to combine it into one view. The two queries look as follows:
1. Select ID, max(mailSent) from Mails where serviceUsed > 0 group by ID
2. Select ID, sum(serviceUsed) from Mails group by ID
Notice that I cannot just combine them both because I also want to show the IDs that have never used my service, i.e., where serviceUsed = 0. Hence, if I just eliminate the where clause in my first query, then I will get wrong results for max(mailSent). Any idea how I can combine both?
In other words what I want is then something like this:
ID, max(mailSent), sum(serviceUsed)
where max(mailSent) is from the first query and sum(serviceUsed) from the second query.
Regards!
Try like this
SELECT * FROM
(
Select ID, max(mailSent) from Mails where serviceUsed > 0 group by ID
UNOIN ALL
Select ID, sum(serviceUsed) from Mails group by ID
) AS T
You can write it within one Query:
SELECT ID, sum(serviceUsed), max(mailSent) from Mails group by ID;
The problem, that you don't have the serviceUsed > 0 in your second Query doesn't matter. You can sum them up too, because they have the value 0.
If you have the following input:
id serviceUsed mailSent
--------------------------
1 0 1.1.1970
1 4 3.1.1970
1 3 4.1.1970
2 0 2.1.1970
The Query should return this result:
id serviceUsed mailSent
--------------------------
1 7 4.1.1970
2 0 2.1.1970
But I wonder, where your primary key is?
You want to do this with conditional aggregation:
select ID, max(case when serviceUsed > 0 then mailSent end),
sum(serviceUsed)
from Mails
group by ID;

sql statement to select previous rows to a search param

Im after an sql statement (if it exists) or how to set up a method using several sql statements to achieve the following.
I have a listbox and a search text box.
in the search box, user would enter a surname e.g. smith.
i then want to query the database for the search with something like this :
select * FROM customer where surname LIKE searchparam
This would give me all the results for customers with surname containing : SMITH . Simple, right?
What i need to do is limit the results returned. This statement could give me 1000's of rows if the search param was just S.
What i want is the result, limited to the first 20 matches AND the 10 rows prior to the 1st match.
For example, SMI search:
Sives
Skimmings
Skinner
Skipper
Slater
Sloan
Slow
Small
Smallwood
Smetain
Smith ----------- This is the first match of my query. But i want the previous 10 and following 20.
Smith
Smith
Smith
Smith
Smoday
Smyth
Snedden
Snell
Snow
Sohn
Solis
Solomon
Solway
Sommer
Sommers
Soper
Sorace
Spears
Spedding
Is there anyway to do this?
As few sql statements as possible.
Reason? I am creating an app for users with slow internet connections.
I am using POSTGRESQL v9
Thanks
Andrew
WITH ranked AS (
SELECT *, ROW_NUMBER() over (ORDER BY surname) AS rowNumber FROM customer
)
SELECT ranked.*
FROM ranked, (SELECT MIN(rowNumber) target FROM ranked WHERE surname LIKE searchparam) found
WHERE ranked.rowNumber BETWEEN found.target - 10 AND found.target + 20
ORDER BY ranked.rowNumber
SQL Fiddle here. Note that the fiddle uses the example data, and I modified the range to 3 entries before and 6 entries past.
I'm assuming that you're looking for a general algorithm ...
It sounds like you're looking for a combination of finding the matches "greater than or equal to smith", and "less than smith".
For the former you'd order by surname and limit the result to 20, and for the latter you'd order by surname descending and limit to 10.
The two result sets can then be added together as arrays and reordered.
I think you need to use ROW_NUMBER() (see this link).
WITH cust1 AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY surname) as numRow FROM customer
)
SELECT c1.surname, c1.numRow, x.flag
FROM cust1 c1, (SELECT *,
case when numRow = (SELECT MIN(numRow) FROM cust1 WHERE surname='Smith') then 1 else 0 end as flag
FROM cust1) x
WHERE x.flag = 1 and c1.numRow BETWEEN x.numRow - 1 AND x.numRow + 1
ORDER BY c1.numRow
SQLFiddle here.
This works, but the flag finally isn't necessary and it would be a query like PinnyM posts.
A variation on #PinnyM's solution:
WITH ranked AS (
SELECT
*,
ROW_NUMBER() over (ORDER BY surname) AS rowNumber
FROM customer
),
minrank AS (
SELECT
*,
MIN(CASE WHEN surname LIKE searchparam THEN rowNumber END) OVER () AS target
FROM ranked
)
SELECT
surname
FROM minrank
WHERE rowNumber BETWEEN target - 10 AND target + 20
;
Instead of two separate calls to the ranked CTE, one to get the first match's row number and the other to read the results from, another CTE is introduced to serve both purposes. Can't speak for PostgreSQL but in SQL Server this might result in a better execution plan for the query, although in either case the real efficiency would still need to be verified by proper testing.

How can I get sets of 2 entries where each set starts with a different letter and no letter is repeated from a sqlite database?

Like this:
apple
aardvark
banana
bet
cow
car
...
zipper
zoo
Assuming the database has more than just two different entries that start with any of the letters. I was thinking of doing something with TOP and wildcards, but I don't really know enough about SQL to pull this off. What can I do?
You can do this with the substr function and a correlated subquery:
SELECT *
FROM YourTable a
WHERE wordField IN (SELECT wordField
FROM YourTable AS b
WHERE substr(a.wordField ,1,1) = substr(b.wordField ,1,1)
ORDER BY wordField
LIMIT 2)
Demo: SQL Fiddle
You can use the ORDER BY to adjust which 2 records are returned. Like ORDER BY RANDOM() if that's supported.