Alternative to ##remserver in TSQL - cross-database

Was hoping to use ##REMSERVER in the definition of a table valued function. But I see it is going away in next version of SQL Server. Any ideas on how to use the identity of the remote [server\instance] to influence the resulting rowset?
e.g.
On server [MAIN]
create view dbo.PartitionedUsers as
Select * from ((Users u inner join Groups g on u.groupid = g.id)
inner join servermap s on s.id = g.serverid and s.servername = ##REMSERVER)
On server [GROUP1]
CREATE SYNONYM dbo.Users as [MAIN].[catalog].[dbo].[PartitionedUsers]
On server [GROUP2]
CREATE SYNONYM dbo.Users as [MAIN].[catalog].[dbo].[PartitionedUsers]
When evaluated on server GROUP1:
SELECT * from dbo.Users
would contain only rows pertaining to groups mapped to that server. Similarly for other servers GROUP2, ..., GROUPN
ta

Related

How to get all the elements of an association?

I have created a CDS views as follows:
define view YGAC_I_REQUEST_ROLE
with parameters
pm_req_id : grfn_guid,
#Consumption.defaultValue: 'ROL'
pm_item_type : grac_prov_item_type,
#Consumption.defaultValue: 'AP'
pm_approval : grac_approval_status
as select from YGAC_I_REQ_PROVISION_ITEM as provitem
association [1..1] to YGAC_I_ROLE as _Role on _Role.RoleId = provitem.ProvisionItemId
association [1..*] to YGAC_I_ROLE_RS as _Relation on _Relation.RoleId1 = provitem.ProvisionItemId
{
key ReqId,
key ReqIdItem,
Connector,
ProvisionItemId,
ActionType,
ValidFrom,
ValidTo,
_Role.RoleId,
_Role.RoleName,
_Role.RoleType,
_Role,
_Relation
}
where
ReqId = $parameters.pm_req_id
and ProvisionItemType = $parameters.pm_item_type
and ApprovalStatus = $parameters.pm_approval
Then I have consumed in ABAP:
SELECT
FROM ygac_i_request_role( pm_req_id = #lv_test,
pm_item_type = #lv_item_type,
pm_approval = #lv_approval
)
FIELDS reqid,
connector,
provisionitemid
INTO TABLE #DATA(lt_result).
How to get the list of _Relation according to selection above.
This is generally not possible like in ABAP SQL queries:
SELECT m~*, kt~*
FROM mara AS m
JOIN makt AS kt
...
This contradicts the whole idea of CDS associations, because they were created to join on-demand and to reduce redundant calls to database. Fetching all fields negates the whole idea of "lazy join".
However, there is another syntax in FROM clause which is enabled by path expressions that allows querying underlining associations both fully and by separate elements. Here is how
SELECT *
FROM ygac_i_request_role( pm_req_id = #lv_test )
\_Role AS role
INTO TABLE #DATA(lt_result).
This fetches all the fields of _Role association into internal table.
Note: remember, it is not possible to fetch all the published associations of current view simultaneously, only one path per query.
Possible workaround is to use JOIN
SELECT *
FROM ygac_i_request_role AS main
JOIN ygac_i_request_role
\_Role AS role
ON main~ProvisionItemId = role~RoleId
JOIN ygac_i_request_role
\_Relation AS relation
ON main~ProvisionItemId = relation~RoleId1
INTO TABLE #DATA(lt_table).
This creates deep-structured type with dedicated structure for every join association like this:
If you are not comfortable with such structure for you task, lt_table should be declared statically to put up all the fields in a flat way
TYPES BEGIN OF ty_table.
INCLUDE TYPE ygac_i_request_role.
INCLUDE TYPE ygac_i_role.
INCLUDE TYPE ygac_i_role_rs.
TYPES END OF ty_table.

EF Core - Count from a specific column

I almost have my EF Core query working... This is the SQL getting produced (notice the Count(*):
SELECT [u].[Key], [u].[Url], [u].[CreatedBy], [u].[CreatedOn], COUNT(*) AS [Clicks]
FROM [URLs] AS [u]
LEFT JOIN [OwnerUrls] AS [o] ON [u].[Key] = [o].[ShortUrlKey]
LEFT JOIN [Clicks] AS [c] ON [u].[Key] = [c].[ShortUrlKey]
GROUP BY [u].[Key], [u].[Url], [u].[CreatedBy], [u].[CreatedOn]
What I need is (have Count look at a specific column/table)
SELECT [u].[Key], [u].[Url], [u].[CreatedBy], [u].[CreatedOn], COUNT(c.ID) AS [Clicks]
FROM [URLs] AS [u]
LEFT JOIN [OwnerUrls] AS [o] ON [u].[Key] = [o].[ShortUrlKey]
LEFT JOIN [Clicks] AS [c] ON [u].[Key] = [c].[ShortUrlKey]
GROUP BY [u].[Key], [u].[Url], [u].[CreatedBy], [u].[CreatedOn]
Here is the EF Query that I'm using...
query = (from u in db.URLs
join ou in db.OwnerUrls on u.Key equals ou.ShortUrlKey into urlOwners
from subSet in urlOwners.DefaultIfEmpty()
join c in db.Clicks on u.Key equals c.ShortUrlKey into urlClicks
from subClicks in urlClicks.DefaultIfEmpty()
group subClicks by new { u.Key, u.Url, u.CreatedBy, u.CreatedOn } into g
select new ShortURL()
{
Key = g.Key.Key,
Url = g.Key.Url,
CreatedBy = g.Key.CreatedBy,
CreatedOn = g.Key.CreatedOn,
Clicks = g.Count()
});
I've tried changing the g.Count() to g.Select(x=>x.Id).Count() and that just causes EF Core to barf and complain about client side evaluation vs server side evaluation etc..
I should mention that the reason I'm joining the first model (OwnerUrls) is to support a where clause that I didn't include here...
Thanks!
I'm not a EF developer, but have worked with SQL Server for a while now. In SQL Server i would use COUNT(DISTINCT c.ID) to eliminate any duplicates you might get from JOINS.
If duplicates are impossible due to the model the COUNT(*) shoud be sufficient.
Maybe this might help:
https://entityframeworkcore.com/knowledge-base/51892585/linq-select-distinct-count-performed-in-memory

MS Access - Run a query displaying one result per user

I've got a database that I'm tying into our access control system. I'm trying to create a report showing the user's first entry and last exit from the building. I've put a query together which hows me the right single result for one user, but I want to automate this to run through a list of users.
The query for grabbing the first entry for one user (as it is currently) is as follows:
SELECT TOP 1 AccessLog.ID, AccessLog.Date, First(AccessLog.Time) AS FirstOfTime, AccessLog.User, AccessLog.Details, AccessLog.Event, AccessLog.Department, AccessLog.Where
FROM AccessLog
GROUP BY AccessLog.ID, AccessLog.Date, AccessLog.User, AccessLog.Details, AccessLog.Event, AccessLog.Department, AccessLog.Where
HAVING (((AccessLog.Date)=#DATE#) AND ((AccessLog.User)="USERNAME") AND ((AccessLog.Where)="Front Door (In)"))
ORDER BY First(AccessLog.Time);
I have a separate table of all the usernames, would it be possible to do a run-as for this?
Basically I'm a complete Access n00b, in the past I'd have done this with PHP and a recurring function but this should be fairly easy to achieve within Access, shouldn't it? Any help much appreciated.
You can get a list of the arrival times for all users by date with the following query. Save the query definition under the name "ArriveTimes":
SELECT AccessLog.Date, AccessLog.User, Min(AccessLog.Time) AS ArriveTime
FROM AccessLog
WHERE (((AccessLog.Where)="Front Door (In)"))
GROUP BY AccessLog.Date, AccessLog.User;
Similarly, you can get the departure times with a similar query saved as "DepartTimes":
SELECT AccessLog.Date, AccessLog.User, Max(AccessLog.Time) AS DepartTime
FROM AccessLog
WHERE (((AccessLog.Where)="Front Door (Out)"))
GROUP BY AccessLog.Date, AccessLog.User;
Then you can join the [ArriveTimes] and the [DepartTimes] queries together (JOIN on [Date] and [User]) and pull the other details by joining on two instances of the [AccessLog] table (JOIN on [Date], [User], and [Time]):
SELECT ArriveTimes.Date, ArriveTimes.User,
ArriveTimes.ArriveTime, AccessLog_A.Details AS ArriveDetails,
DepartTimes.DepartTime, AccessLog_D.Details AS DepartDetails
FROM
(AccessLog AS AccessLog_A INNER JOIN
(ArriveTimes INNER JOIN DepartTimes
ON (ArriveTimes.User = DepartTimes.User)
AND (ArriveTimes.Date = DepartTimes.Date)
)
ON (ArriveTimes.ArriveTime = AccessLog_A.Time)
AND (AccessLog_A.User = ArriveTimes.User)
AND (AccessLog_A.Date = ArriveTimes.Date)
)
INNER JOIN AccessLog AS AccessLog_D
ON (DepartTimes.DepartTime = AccessLog_D.Time)
AND (DepartTimes.User = AccessLog_D.User)
AND (DepartTimes.Date = AccessLog_D.Date);

dynamic table name in a query

How can I put a table name dynamically in a query?
Suppose I have a query as shown below:
Select a.amount
,b.sal
,a.name
,b.address
from alloc a
,part b
where a.id=b.id;
In the above query I want to use a table dynamically (part b if the database is internal, p_part b if the database if external).
I have a function that returns which database it is. Suppose the function is getdatabase();
select decode(getdatabase(),'internal','part b','external','p_part b')
from dual;
How can I use this function in my main query to insert the table name dynamically into the query?
I don't want to implement this using the primitive way of by appending strings to make a final query and then open cursor with that string.
I don't want to implement this with primitive way of by appending
strings to make a final query and then open cursor with that string .
That's really the only way you can do it. It's not possible to use a variable or function call for the table name when using a regular PL/SQL SQL block, you have to use dynamic SQL.
Refer to Oracle documentation for more details:
http://docs.oracle.com/cd/B10500_01/appdev.920/a96590/adg09dyn.htm
Here's an example from the doc:
EXECUTE IMMEDIATE 'SELECT d.id, e.name
FROM dept_new d, TABLE(d.emps) e -- not allowed in static SQL
-- in PL/SQL
WHERE e.id = 1'
INTO deptid, ename;
You can do this without dynamic SQL, assuming both tables (part and p_part) are available at compile time:
select a.amount
,b.sal
,a.name
,b.address
from alloc a
,part b
where a.id=b.id
and (select getdatabase() from dual) = 'internal'
UNION ALL
select a.amount
,b.sal
,a.name
,b.address
from alloc a
,p_part b
where a.id=b.id
and (select getdatabase() from dual) = 'external'
;
I've put the function call in a subquery so that it is run only once per call (i.e. twice, in this instance).

Special Select Statement ( sqlite )

Table: "user"
- Userid - Name -
(every userid is unique)
Table: "money spend"
- Userid - money spend -
(a userid may have several entries with different "money spend")
Now, I need the total sum of the money spend by a user.
To conclude, I need the following view:
- name - sum (money spend) -
Wich statement may give me this result?
You can use an aggregate function and group by:
select u.name, sum(ms.money)
from user u, money_spend ms
where u.userid = ms.userid
group by u.userid
Note that this here assumes that every user has at least 1 row in the money_spend table: http://www.sqlite.org/lang_aggfunc.html
Due to the way that aggregate functions work, you could set up the money_spend table with a 0 value for each user so you don't run into any issues :)
Because you might have users without any entry in table money_spend you need an outer join:
select n.name, sum(ms.money)
from user n
left outer join money_spend ms on (n.userid = ms.userid)
group by n.name
Edit: To be sure this all works I just tried this
create table user(userid, name);
insert into user values (1, 'user1');
insert into user values (2, 'user2');
insert into user values (3, 'user3');
create table moneyspend(userid, amount);
insert into moneyspend values (1,10);
insert into moneyspend values (1,20);
insert into moneyspend values (2,100);
select * from user;
select * from moneyspend;
select u.name, sum(m.amount)
from user u
left outer join moneyspend m on (u.userid = m.userid)
group by u.name;
drop table user;
drop table moneyspend;
The console output is the following (testSQLite is my test DB, testsql.sql is the above)
hols-Mac:div4 hol$ sqlite3 -init testsql.sql testSQLite
-- Loading resources from testsql.sql
1|user1
2|user2
3|user3
1|10
1|20
2|100
user1|30
user2|100
user3|
SQLite version 3.6.12
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite>

Resources