Select row if you are current user or substitute - sql-server

Categories
Category
person
A
Kate
B
Tom
Substitutions
Person
Substitute
Kate
Roxy
I want to get Category, where person is me or my substitute.
I have variable with current user, so when current user is Kate is okey:
Select category
from Categories
where person = ‘Kate’
But if current user is Roxy, I still want to see the same row.
It should also work when current user not exists in Substitutions table.

Considering the lack of sample data, I think an EXISTS would likely be the safest bet here:
SELECT C.Category,
C.person
FROM dbo.Category C
WHERE EXISTS (SELECT 1
FROM dbo.Substitution S
WHERE C.person IN (S.Person, S.Substitute)
AND #YourVariable IN (S.Person, S.Substitute))
OR C.person = #YourVariable;

Related

Apex Get Account Record Ids where Contact First Name is in LIST ( using AND Condition )

In Salesforce I would like to retrieve AccountIds where its related Contact FirstName must have values in list of String. List of string is {'Test','User'}. I would need to find all Accounts where Contact first Name has both Test and User as related Contacts,
I am trying as below but the below query will show accounts where even 1 value matches as Contact First Name.
List<String> names = new List<String>{'Test','User'}; List<Account> accountList =         [ Select Id from Account Where Id IN         (Select AccountId FROM Contacts where FirstName LIKE :names)];
Please help
SELECT Id, Name
FROM Account
WHERE Id IN (SELECT AccountId FROM Contact WHERE LastName = 'Test')
AND Id IN (SELECT AccountId FROM Contact WHERE LastName = 'User')
But it'll work only 2 times, you can't write 3rd "IN" like that (see here).
If you need a more generic solution that can take lists of any size you'd have to run "my" queries in loop, 2 names at a time, save results to some Set<Id> or Map, play with functions like myset.retainAll(idsFromCurrentLoopQuery)... Of course query in a loop is bit evil too.

SQL Server - Update All Records, Per Group, With Result of SubQuery

If anyone could even just help me phrase this question better I'd appreciate it.
I have a SQL Server table, let's call it cars, which contains entries representing items and information about their owners including car_id, owner_accountNumber, owner_numCars.
We're using a system that sorts 'importantness of owner' based on number of cars owned, and relies on the owner_numCars column to do so. I'd rather not adjust this, if reasonably possible.
Is there a way I can update owner_numCars per owner_accountNumber using a stored procedure? Maybe some other more efficient way I can accomplish every owner_numCars containing the count of entries per owner_accountNumber?
Right now the only way I can think to do this is to (from the c# application):
SELECT owner_accountNumber, COUNT(*)
FROM mytable
GROUP BY owner_accountNumber;
and then foreach row returned by that query
UPDATE mytable
SET owner_numCars = <count result>
WHERE owner_accountNumber = <accountNumber result>
But this seems wildly inefficient compared to having the server handle the logic and updates.
Edit - Thanks for all the help. I know this isn't really a well set up database, but it's what I have to work with. I appreciate everyone's input and advice.
This solution takes into account that you want to keep the owner_numCars column in the CARs table and that the column should always be accurate in real time.
I'm defining table CARS as a table with attributes about cars including it's current owner. The number of cars owned by the current owner is de-normalized into this table. Say I, LAS, own three cars, then there are three entries in table CARS, as such:
car_id owner_accountNumber owner_numCars
1 LAS1 3
2 LAS1 3
3 LAS1 3
For owner_numCars to be used as an importance factor in a live interface, you'd need to update owner_numCars for every car every time LAS1 sells or buys a car or is removed from or added to a row.
Note you need to update CARS for both the old and new owners. If Sam buys car1, both Sam's and LAS' totals need to be updated.
You can use this procedure to update the rows. This SP is very context sensitive. It needs to be called after rows have been deleted or inserted for the deleted or inserted owner. When an owner is updated, it needs to be called for both the old and new owners.
To update real time as accounts change owners:
create procedure update_car_count
#p_acct nvarchar(50) -- use your actual datatype here
AS
update CARS
set owner_numCars = (select count(*) from CARS where owner_accountNumber = #p_acct)
where owner_accountNumber = #p_acct;
GO
To update all account_owners:
create procedure update_car_count_all
AS
update C
set owner_numCars = (select count(*) from CARS where owner_acctNumber = C.owner_acctNumber)
from CARS C
GO
I think what you need is a View. If you don't know, a View is a virtual table that displays/calculates data from a real table that is continously updated as the table data updates. So if you want to see your table with owner_numCars added you could do:
SELECT a.*, b.owner_numCars
from mytable as a
inner join
(SELECT owner_accountNumber, COUNT(*) as owner_numCars
FROM mytable
GROUP BY owner_accountNumber) as b
on a.owner_accountNumber = b.owner_accountNumber
You'd want to remove the owner_numCars column from the real table since you don't need to actually store that data on each row. If you can't remove it you can replace a.* with an explicit list of all the fields except owner_numCars.
You don't want to run SQL to update this value. What if it doesn't run for a long time? What if someone loads a lot of data and then runs the score and finds a guy that has 100 cars counts as a zero b/c the update didn't run. Data should only live in 1 place, updating has it living in 2. You want a view that pulls this value from the tables as it is needed.
CREATE VIEW vOwnersInfo
AS
SELECT o.*,
ISNULL(c.Cnt,0) AS Cnt
FROM OWNERS o
LEFT JOIN
(SELECT OwnerId,
COUNT(1) AS Cnt
FROM Cars
GROUP BY OwnerId) AS c
ON o.OwnerId = c.OwnerId
There are a lot of ways of doing this. Here is one way using COUNT() OVER window function and an updatable Common Table Expression [CTE]. That you won't have to worry about relating data back, ids etc.
;WITH cteCarCounts AS (
SELECT
owner_accountNumber
,owner_numCars
,NewNumberOfCars = COUNT(*) OVER (PARTITION BY owner_accountNumber)
FROM
MyTable
)
UPDATE cteCarCounts
SET owner_numCars = NewNumberOfCars
However, from a design perspective I would raise the question of whether this value (owner_numCars) should be on this table or on what I assume would be the owner table.
Rominus did make a good point of using a view if you want the data to always reflect the current value. You could also use also do it with a table valued function which could be more performant than a view. But if you are simply showing it then you could simply do something like this:
SELECT
owner_accountNumber
,owner_numCars = COUNT(*) OVER (PARTITION BY owner_accountNumber)
FROM
MyTable
By adding a where clause to either the CTE or the SELECT statement you will effectively limit your dataset and the solution should remain fast. E.g.
WHERE owner_accountNumber = #owner_accountNumber

SQL - Insert multiple records from select

I have been searching all day but could not find answer to this:
I have a table on SQL Server:
dbo.Program
with fields:
Program.id...PK autoincrement
Program.user...varchar
Program.program...varchar
Program.installed...boolean
Program.department...varchar
Program.wheninstalled...date
Now, I want to insert a new record for every distinct user and copy the department from his latest(Program.wheninstalled) record with other values the same for every user:
Program.user...every unique user
Program.program...MyMostAwesomeProgram
Program.installed...false
Program.department...department of the record with the latest program.wheninstalled field of all the records of the unique user in program.user
Program.wheninstalled...null
I know how to do it in an ugly way:
select the latest records for every user and their department in that record
extract values from 1) and make it into insert into
(field1, field2...fieldX) values
(1records_value1, 1records_value2...1records_valueX),
(2records_value1, 2records_value2...2records_valueX),
...
(Nrecords_value1, Nrecords_value2...Nrecords_valueX)
but I would like to know how to do it in a better way. Oh I cannot use some proper HR databse to make my life easier so this is what I got to work with now.
I'm a postgres guy, but something akin to the below should work:
insert into Program (user, program, installed, department, wheninstalled)
select user,
'MyMostAwesomeProgram',
false,
(select department from someTable where u.user = ...),
null
from users as u;
from https://stackoverflow.com/a/23905173/3430807
you said you know how to do the select
insert into Program (user, program, installed, department, wheninstalled)
select user, 'MyMostAwesomeProgram', 'false' , department, null
from ...
This should do it:
insert into Program (user, program, installed, department, whenInstalled)
select user, program, installed, department, whenInstalled
from
(
select User
, 'MyMostAwesomeProgram' program
, 0 installed
, department
, null whenInstalled
, row_number() over (partition by user order by whenInstalled desc, id desc) r
from Program
) p
where p.r = 1
The interesting bit is the row_number() over (partition by user order by whenInstalled desc, id desc) r.
This says to return a column, r, which holds values 1..n for each user, counting up according to the order by clause (i.e. starting with the most recent whenInstalled and working backwards).
I also included the id field in the order by clause in case there were two installs for the same user on the same date; in such a case the most recently added (the one with the higher id is used first).
We then put this in a subquery, and select only the first record; thus we have 1 record per user, and it's the most recent.
The only values we use from this record are the user and department fields; all else is defined per your defaults.
So I am gonna answer this for some other people who might google this:
To get a records from a table to another table you need to use Select into statement
I like using With XXX as ( some select) to specify, say, "virtual" table with which you can work during the query
As JohnLBevan meantioned a very useful function Row_number, over, partition by and what i missed is a rank
So once you read on these you should be able to understand how to do what I wanted to do.

Database Design with a flat considering time

Background: School counselors will login and make comments about their meetings with students.
Issues: PHP 4 server
Flat File or CSV
Suppose I have HTML form with the following text fields
user id
date
comments
How can I create an effective record keeping to be able to display the comments that have been made and next to them the date that they were made.
Without having more information this is my first shot. I focused on the database design aspect of your question since that is what it is tagged as. If you want to know how it should be displayed in a PHP app that seems like another question.
Student
-------
ID
FName
LName
{Other Student Info}
Counselor
---------
ID
FName
LName
Meeting
-------
ID
StudentId
CounselorId
Date
MeetingComment
-------
MeetingId
Comment
With this structure your query would look like the one below to select all comments and their date for one student.
SELECT mc.Comment, m.Date
FROM MeetingComment as mc
INNER JOIN Meeting as m
ON mc.MeetingId = m.MeetingId
WHERE m.StudentId = 1234

Outputting Results from complicated database structure (SQL Server)

This will be a long question so I'll try and explain it as best as I can.
I've developed a simple reporting tool in which a number of results are stored and given a report id, these results were generated from a particular quote being used on the main system, with a huge list of these being stored in a quotes table. Here are the current batch:
REPORTS
REP_ID DESC QUOTE_ID
-----------------------------------
1 Test 1
2 Today 1
3 Last Week 2
RESULTS
RES_ID TITLE REFERENCE REP_ID
---------------------------------------------------
1 Equipment Toby 1
2 Inventory Carl 1
3 Stocks Guest 2
4 Portfolios Guest 3
QUOTE
QUOTE_ID QUOTE
------------------------------------
1 Booking a meeting room
2 Car Park Policy
3 New User Guide
So far, so good, a simple stored procedure was able to pull all the information necessary.
Now, the feature list has been upped to include categories and groups of the quotes. In the Reports table quote_id has been changed to group_id to link to the following tables.
REPORTS
- REPORT_ID
- DESC
- GROUP_ID
GROUP
- GROUP_ID
- GROUP
GROUP_CAT_JOIN
- GCJ_ID
- CAT_ID
- GROUP_ID
CATEGORIES
- CAT_ID
- CATEGORY
CAT_QUOTE_JOIN
- CQJ_ID
- CAT_ID
- QUOTE_ID
The idea of these changes is so that instead of running a report on a quote I should now write a report for a group where a group is a set of quotes for certain occasions. I should also be able to run a report on a category where a category is also a set of quotes for certain departments. The trick is that several categories can fall into one group.
To explain it further, the results table has a report_id that links to reports, reports has a group_id that links to groups, groups and categories are linked through a group_cat_join table, the same with categories and quotes through a cat_quote_join table.
In basic terms I should be able to pull all the results from either a group of quotes or a category of quotes. The query will aim to pull all the results from a certain report under either a certain category, a group or both. This puzzle has left me stumped for days now as inner joins don't appear to be working and I'm struggling to find other ways to solve the problem using SQL.
Can anyone here help me?
Here's some extra clarification.
I want to be able to return all the results within a category, but as of right now the solution below and the ones I've tried always output every solution within a description, which is not what I want.
Here's an example of the data I have in there at the moment
Results
RES_ID TITLE REFERENCE REP_ID
---------------------------------------------------
1 Equipment Toby 1
2 Inventory Carl 1
3 Stocks Guest 2
4 Portfolios Guest 3
Reports
REP_ID DESC GROUP_ID
-----------------------------------
1 Test 1
2 Today 1
3 Last Week 2
GROUP
GROUP_ID GROUP
---------------------------------
1 Standard
2 Target Week
GROUP_CAT_JOIN
GCJ_ID GROUP_ID CAT_ID
----------------------------------
1 1 1
2 1 2
3 2 3
CATEGORIES
CAT_ID CAT
-------------------------------
1 York Office
2 Glasgow Office
3 Aberdeen Office
CAT_QUOTE_JOIN
CQJ_ID CAT_ID QUOTE_ID
-----------------------------------
1 1 1
2 2 2
3 3 3
QUOTE
QUOTE_ID QUOTE
------------------------------------
1 Booking a meeting room
2 Car Park Policy
3 New User Guide
This is the test data I am using at the moment and to my knowledge it is similar to what will be run through once this is done. In all honesty I'm still trying to get my head around this structure.
The result I am looking for is if I choose to search by group I'll get everything within a group, if I choose everything inside a category I get everything just inside that category, and if I choose something from a category in a group I get everything inside that category. The problem at the moment is that whenever the group is referenced everything inside every category that's linked to the group is pulled.
The following will get the necessary rows from the results:
select
a.*
from
results a
inner join reports b on
a.rep_id = b.rep_id
and (-1 = #GroupID or
b.group_id = #GroupID)
and (-1 = #CatID or
b.cat_id = #CatID)
Note that I used -1 as the placeholder for all Groups and Categories. Obviously, use a value that makes sense to you. However, this way, you can specify a specific group_id or a specific cat_id and get the results that you want.
Additionally, if you want Group/Category/Quote details, you can always append more inner joins to get that info.
Also note that I added the Group_ID and Cat_ID conditions to the Reports table. This would be the SQL necessary if and only if you add a Cat_ID column to the Reports table. I know that your current table structure doesn't support this, but it needs to. Otherwise, as my grandfather used to say, "Boy, you can't get there from here." The issue here is that you want to limit reports by group and category, but reports only knows about group. Therefore, we need to tie something to the category from reports. Otherwise, it will never, ever, ever limit reports by category. The only thing that you can limit by both group and category is quotes. And that doesn't seem to be your requirement.
As an addendum: If you add cat_id to results instead of reports, the join condition should be:
and (-1 = #CatID or
a.cat_id = #CatID)
Is this what you are looking for?
SELECT a.*
FROM Results a
JOIN Reports b ON a.REP_Id = c.REP_Id
WHERE EXISTS (
SELECT * FROM CAT_QUOTE_JOIN c
WHERE c.QUOTE_ID = b.QUOTE_ID -- correlation to the outer query
AND c.CAT_ID = #CAT_ID -- parameterization
)
OR EXISTS (
-- note that subquery table aliases are not visible to other subqueries
-- so we can reuse the same letters
SELECT * FROM CAT_QUOTE_JOIN c, GROUP_CAT_JOIN d
WHERE c.CAT_ID = d.CAT_ID -- subquery join
AND c.QUOTE_ID = b.QUOTE_ID -- correlation to the outer query
AND d.GROUP_ID = #GROUP_ID -- parameterization
)

Resources