I have a table with emails (called Mails) and I want to mark particular messages as assigned to different teams of workers (eg. team01, team02 etc). The msg<->team relations are kept in another table (called MailAssignments). I'm trying to get messages which are marked as being for just one team (let's say team01) and the message ID should be less than a specified value so that the set of messages is narrowed down to, lets say, 10.
I've got the below query but it always shows ALL messages assigned to the team isntead of showing only the ones with msgId < specified.
What is wrong with my query?
string queryGetMails =
"SELECT TOP 10 * FROM Mails WHERE msgId IN (SELECT msgId FROM MailAssignments WHERE (forTeam='team01') AND (msgId < ?) )";
I've also tried this (and the result is the same) :/
"SELECT TOP 10 * FROM Mails WHERE (msgId<?) AND (msgId IN (SELECT msgId FROM MailAssignments WHERE forTeam='team01' ) )";
Why do you insist with subqueries and in clause when it seems that you have a clear join between the two tables? Have you tried this?
SELECT TOP 10 Mails.*
FROM Mails left join
MailAssignments On Mails.msgID = MailAssignments.msgID
WHERE Mails.msgId < ? AND MailAssignments.forTeam = 'team01'
Related
I am trying to create a query to help get a list of accounts from an existing database. I will have two lists of integers passed in through two Table Value Parameters(TVP) from C#. I then need to see if multiple columns have any of the values in the corresponding TVP tables. The TVP lists of integers are provided by different clients and may differ between clients. That is why they are TVP's to allow the values to be passed in as parameters.
The data structure cannot be changed, it is created based on data from another system. Comments about changing the data structure won't be helpful. To help I will talk about an example table that would help show what I need.
Looking at a table like the following:
Table Accounts
varchar(200) AccountId
int StatusId1
int StatusId2
int StatusId3
int StatusId4
int Identifier1
int Identifier2
int Identifier3
int Identifier4
int Identifier5
I know that I can do a sql statement like:
Select AccountId from Accounts where StatusId1 In (1,2,3)
I was able to learn that I can reverse the In command as well:
Select AccountId from Accounts where 1 In (StatusId1, StatusId2, StatusId3, StatusId4)
This only lets me check one value against each column. The problem is I need to mix the two while using the TVP for the list of integers.
The closest I have been able to create is the following:
--Load the TVP lists
SELECT * INTO #StatusCodes FROM #StatusId
SELECT * INTO #IdentityCodes FROM #IdentifierId
--Find the Accounts that have the chosen Ids
SELECT AccountId
FROM Accounts
WHERE StatusId1 IN( SELECT Id FROM #StatusCodes)
OR StatusId2 IN( SELECT Id FROM #StatusCodes)
OR StatusId3 IN( SELECT Id FROM #StatusCodes)
OR StatusId4 IN( SELECT Id FROM #StatusCodes)
OR Identifier1 IN (SELECT Id FROM #IdentityCodes)
OR Identifier2 IN (SELECT Id FROM #IdentityCodes)
OR Identifier3 IN (SELECT Id FROM #IdentityCodes)
OR Identifier4 IN (SELECT Id FROM #IdentityCodes)
OR Identifier5 IN (SELECT Id FROM #IdentityCodes)
This query worked in my prototype and I got back the list of accounts that had at least one of these ids. I see a lot of select statements and it doesn't look very good. I am not sure how well it performs either. I am wondering if there is a better way to do this?
This is for a system that creates a report based on conditions our clients make. Each client runs from a couple to 100 reports each night. That means this is run possibly hundreds of times each night. While it isn't a system running thousands of times per hour, it does process a lot of data. Some of the databases it will search will be big with lots of accounts to search.
One option uses exists:
select a.acountId
from accounts a
where
exists (
select 1
from #StatusCodes s
where s.id in (a.StatusId1, a.StatusId2, a.StatusId3, a.StatusId4)
)
or exists (
select 1
from #IdentityCodes i
where i.id in (a.Identifier1, a.Identifier2, a.Identifier3, a.Identifier4)
)
I am creating a patient booking system using ASP.NET.
I want to limit the number of bookings to 25 per day.
Using a suggestion, I have obtained the following SQL Trigger Code:
CREATE TRIGGER trg_CheckAppointmentCount ON dbo.schedule_master AFTER
INSERT,UPDATE
AS BEGIN
IF EXISTS (SELECT 1
FROM dbo.schedule_master YT
WHERE YT. Schedule_Date IN (SELECT i Schedule_Date FROM inserted)
GROUP BY YT. Schedule_Date
HAVING COUNT(YT.KeyColumn) > 25)
THROW 50012, N'Cannot have more than 25 appointments on a single day.', 16;
END;
GO
But I don't have a KeyColumn. Please suggest any changes.
This would better handled in the SQL Server side of things, in my opinion. you won't be able to do this with a CONSTRAINT but you could use a trigger. This is Pseudo-SQL, but perhaps something like this:
CREATE TRIGGER trg_CheckAppointmentCount ON dbo.YourTable
AFTER INSERT,UPDATE
AS BEGIN
IF EXISTS (SELECT 1
FROM dbo.YourTable YT
WHERE YT.DateColumn IN (SELECT i.DateColumn FROM inserted i)
GROUP BY YT.DateColumn
HAVING COUNT(YT.KeyColumn) > 25)
THROW 50012, N'Cannot have more than 25 appointments on a single day.', 16;
END;
GO
I wonder why would you limit the number of booking; However; this can be acheived just by a simple counter in your backend system. Perhasps some configuration of max record so that you can change it in future rather just limit it to 25. So get the total records created while new booking is getting created; and if it's > than config limit ( in this case 25) , just display a message back to the user.
Hope this helps you to build the logic.
I have a sql (transact sql - SQL server 2012) which used to fetch names of customers from a table (Customer) who has valid addresses (from table Details):
Select Customer.Name, Details.Address
from Customer
left outer join Details on Details.Customer = Customer.Name
This used to send back each record (name) row for each customer every time from the db server. No multiple records are fetched.
Recently I needed to modify this sql text in order to fetch even the name of the books they have borrowed as per the database, which is saved in another table (Lending). Now the script looks like:
Select Customer.Name, Details.Address, Lending.BookName
from Customer
left outer join Details on Details.Customer = Customer.Name
left outer join Lending on Lending.CustomerName = Customer.Name
It is returning the records properly, but now I have got a problem. Since a customer can borrow multiple books, the returned data has multiple rows for the same customer showing multiple book names. According to my software specification I need to fetch one line for each customer and in that one row i need to append all the book names in a single column.
Can someone help me with this: How to append multiple data for same record in a single column such as:
Name Address BookName
Somdip XX Brief History of Time,Headfirst SQL,Headfirst C#
instead of
Name Address BookName
Somdip XX Brief History of Time
Somdip XX Headfirst SQL
Somdip XX Headfirst C#
??
I used the above sql text with 'where' and 'order by' clauses such as :
SELECT Name,
Address ,
Split.a.value('.', 'VARCHAR(100)') BookName
FROM (SELECT Name,
Address ,
Cast ('<M>' + Replace(BookName, ',', '</M><M>') + '</M>' AS XML) AS Data
FROM [table] where ID = '1' order by Name) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)
and it is giving me an error: The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified.
try this:
SELECT Name,
Address ,
Split.a.value('.', 'VARCHAR(100)') BookName
FROM (SELECT Name,
Address ,
Cast ('<M>' + Replace(BookName, ',', '</M><M>') + '</M>' AS XML) AS Data
FROM [table]) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)
While I think this is generally a bad idea - returning multiple data items in a single cell - there are a number of ways to go about it, with different performance concerns.
What you're looking for is here: Concatenate many rows into a single text string?
I'm trying to make a news table, with the latest news from my site.
In my news site, I only want to show the first 20 words from the table (with a "Read more")
I'm selecting my table like this:
cmd.CommandText = "SELECT news.*, login.firstname, login.lastname, login.pisture AS picture FROM news LEFT JOIN login ON login.Id = nyheder.writer ORDER BY news.Id DESC";
If you use sql server, try to add SUBSTRING ( expression ,start , length ) to your query:
e.g. if you want to get the 20 characters from the "content" field in "news" table:
SELECT news.*,
SUBSTRING ( news.content, 1, 20) as short_content ,
login.firstname, login.lastname,
login.pisture AS picture
FROM news LEFT JOIN login ON login.Id = nyheder.writer
ORDER BY news.Id DESC
If you use another database like oracle try "substr".
Usually to retrieve a short part of a long text in database, you should use a string function like as this:
SELECT LEFT(news.newsText, 20)+"..." as shortText ,... from ...
the LEFT function works in SqlServer and Ms Access database. for other databases there is an equivalent function. if you want you can check also the length of news content and add the "..." trailing string only if it is trimmed.
I'm using an MS Access .mdb database in my C# application. The database contains email messages (one row - one message).
I need to get a specified amount of messages which are older than a specified datetime. Let's say, 30 messages before 2012-02-01 12:00:00. I've tried different queries but all of them give me errors. Have tried the TOP, LIMIT and other statements also:
"SELECT * FROM ( SELECT * FROM Mails WHERE (timeReceived < ?) ) LIMIT 0,30";
"SELECT * FROM Mails WHERE (timeReceived = ?) ORDER BY timeReceived DESC LIMIT ?";
etc.
Any hints appriciated.
You say you've tried TOP clause, but it should work
SELECT TOP 30 * FROM Mails WHERE timeReceived < '2012-02-01 12:00:00' ORDER BY timeReceived DESC
You must take this into account.
The top directive doesn't return the top n items, as one is easily led
to believe. Instead it returns at least n distinct items determined by
the ordering of the result.
Edit to clarify:
SELECT TOP 25
FirstName, LastName
FROM Students
WHERE GraduationYear = 2003
ORDER BY GradePointAverage DESC;
http://office.microsoft.com/en-us/access-help/results.aspx?qu=top&ex=1&origin=HA010256402
The TOP predicate does not choose between equal values. In the
preceding example, if the twenty-fifth and twenty-sixth highest grade
point averages are the same, the query will return 26 records.
So, no, rows with the same timestamp are not skipped. But if the 30th and 31th records(according to the order clause) have the same timestamp, both will be returned and you get 31 records.
If you want to force 30 records to be returned, you need to include the primary key into the Order By to differentiate between tied values:
SELECT TOP 30 *
FROM Mails
WHERE timeReceived < '2012-02-01 12:00:00'
ORDER BY timeReceived DESC, MailID ASC
You can try this SQL out:
SELECT top 30 * FROM Mails WHERE timeReceived < #2012-02-01#
This should work (unverified):
SELECT top 30 *
FROM Mails
WHERE timeReceived < '2012-02-01 12:00:00'
ORDER BY timeReceived desc