SQL - Ordering the results of a recursive relationship - sql

I've got a databases table of account users. There are two types of account:-
Administrator Account
Standard Account
The data table has two additional columns, Account Number and Parent Account Number. Every record regardless gets assigned a new Account Number, but if an account is a Standard Account, then it gets assigned a Parent Account Number. I can tell who is an administrator by the fact that the Parent Account Number field is NULL.
I'm wanting to print a list of these users, ordered by the administrator account and then any children of that administrator, before moving onto the next administrator account. I'm expecting a list of:-
Administrator (Account 1250, Parent NULL)
Standard Account (Account 1255, Parent 1250)
Standard Account (Account 1256, Parent 1250)
Administrator (Account 1375, Parent NULL)
Standard Account (Account 1403, Parent 1375)
I've did a SQL query of:-
SELECT *
FROM [LWC].[dbo].[AspNetUsers]
ORDER BY AccountNumber, ParentAccountNumber
but this isn't ordering correctly, because every Account Number is different. I presume this would sort as I expected it to, if the AccountNumber was the same for multiple records but it isn't.
Can anyone suggest how I can sort this correctly?
Thanks!

I think simply:
ORDER BY ISNULL(ParentAccountNumber, AccountNumber), ParentAccountNumber, AccountNumber
would do what you want.

As this is a recursive relationship a recursive cte could be used , or a hierarchyId type would / may be better.

Try something like this:
select * from
(SELECT *,
case
when Parent is null then cast((cast(Account as nvarchar) + '0') as int)
else cast((cast(Parent as nvarchar) + '1') as int)
end as OrderCol
FROM [LWC].[dbo].[AspNetUsers])
order by OrderCol

Related

Oracle query - How to check if a user is the only one assigned to a project

I am trying to determine if a user is assigned to any project with a specific role (ProjMemberTypeID) and if so, if s/he is the only one.
There is "projMember" table with Project ID, User ID and Project Member Type ID. I need to know if there are any project IDs where this user is the only one with member type id of 1.
Is it also possible to get some project info (like name, ...) joining the requested query with "Project" table, based on Project ID?
I tried this but it only gives me how many users are assigned to projects and if I filter by user ID I get how many projects user is assigned to but don't know how to check if s/he is the only one.
select count(userid), projectid
from projmember
where projmembertypeid = 1 -- and userid=73
group by projectid
order by projectid;
You can use a having clause:
select projectid
from projmember
where projmembertypeid = 1
group by projectid
having min(userid) = 73 and max(userid) = min(userid)
order by projectid;
The query filters on the relevant member type, then aggregates by project; all that is left to do is ensure that the given user was found, and no other.

Setting a column value for only one user depending on results

I'm currently playing around with SQL and trying to find the best way to accomplish this:
I currently have a user table that has a user_id, organisation_id, registered_datetime. There are
a number of users in this table with different organisations. There may be 3 different users in
1 organisation, or 1 in 1 organisation, etc.
I have added a new column called admin_user and I am trying to string up an SQL statement together
to update the admin user column. There can only be one admin user per organisation, and I want
the user who registered the earliest for that organisation to be the admin.
I could do this manually but it would take time if I had a lot of users. What would be the best
way to accomplish this?
EDIT:
So I have a number of users like this with the columns. The ones highlighted are the users that has registered the earliest. I want to be able to set those users as an admin user. The only admin user within their organisation and set the rest to 0. 1 (Admin) 0 (Not Admin)
This SQL query will mark users which registered_datetime are lowest in its organisation_id as admin.
UPDATE users SET admin_user = 1
WHERE user_id IN (
SELECT u.user_id FROM users u
WHERE u.registered_datetime IS NOT NULL AND NOT EXISTS(
SELECT 1 FROM users iu
WHERE iu.organisation_id = u.organisation_id AND iu.registered_datetime < u.registered_datetime
)
)
You might want to update all users to admin_user = 0 before this code, so all your users will have their values set.
One caveat here, if two users in one organisation were registered in exact same time, then both of them will be marked as administrators.
Update
I have added u.registered_datetime IS NOT NULL into the WHERE clause to filter out users with NULL in registered_datetime.
MSSQL
In MsSql server I usually solve this problem a in another way, by using ROW_NUMBER():
WITH base AS (
SELECT user_id, ROW_NUMBER() OVER ( PARTITION BY organisation_id ORDER BY registered_datetime ASC ) AS rn
FROM user
WHERE registered_datetime IS NOT NULL
)
UPDATE user SET is_admin = 1
WHERE user_id IN (
SELECT base.user_id FROM base WHERE rn = 1
)
This is too long for a comment.
You are describing three different tables:
Users
Organizations
UserOrganizations
The last has one row per user and per organization. This provides the mapping between the two. This can be called a "mapping" table, "junction" table, or "association" table.
How you implement one admin per organization depends on the database you are using.
You do not need the admin_user column. You need a column isadmin.
When a user is registered, if he is the first in the organization, then the isadmin column has the value 1, otherwise 0
Also, you can use the AAA query to find the administrator
SELECT `table`.`user_id`, MIN(`table`.`registered_datetime`) WHERE `organisation_id`=...

Create dynamic Metric, which will be based on various dimensions/ filters applied

I have a table "Trans" which contains the acccountNumbers and other dimensions like Facility , Status etc.
I need to create a calculated member in SSAS where the logic would be
Count of Accounts in a group / Total accounts.
Count of Accounts in a group would be based on the Dimension filter I provide.
For e.g. If I provide the facility then I need the Count of accounts (In numerator) group be different facilities.
Likewise If I provide the Status I would need the numerator to be grouped as per the data in Status table.
Table Name
Trans (AccountNumber, facility,Status) -- This is fact table
Dimension tables
Facility( Id, Facility_name)
Status (Id, Status)
Not sure how to go about it.
EXISTING is a useful function, so maybe something like:
COUNT(
EXISTING [AccountNumber].[AccountNumber].MEMBERS
)

SQL Insert based on role

I have a database with 3 tables:
Table 1 (Department) - This is a table with columns for departments and departmentID's
Table 2 (SecurityMap) - This is a table that maps rolenames to department ID's
Table 3 (customer info) - this is the info that is displayed to users based on their role memberships
I have all of the SELECT based on role functions working.
What I need is to figure out how to insert a specific value into the DepartmentID column within Table 3 by default, based on the users role membership. For instance, when someone is adding a new row to the database - in addition to the data they are supplying within the "add" form, I need a default value inserted into this column. If they are a member of the Marketing role, it should be a 1, if they are a member of the IT role, it should be a 2, etc...
Ideally, this would be done without any knowledge to the user that it is even happening. I would assume that I need to use an "Instead Of" trigger, but I have no idea how to proceed....
Shouldn't be too difficult:
In your app, keep track of the logged-in user and their role.
When your app saves the customer data, make sure it passes the database the role ID as well as the user-entered data (a stored procedure would be ideal here)
When the database processes the supplied data, it saves the role ID into the appropriate column.
this should work provided the rolename-column (or id) is unique in the securityMap-table otherwise the select could return more than one value, maybe you need to select the departmentId differently then.
insert into customer_info(otherdata, departmentId) values('data', (select departmentId from securityMap where rolename = 'userrole'))
Edit:
since you mentioned db_owner maybe this can help you (from http://www.sqlservercentral.com/Forums/Topic411310-338-1.aspx)
WITH CTE_Roles (role_principal_id)
AS
(
SELECT role_principal_id
FROM sys.database_role_members
WHERE member_principal_id = USER_ID()
UNION ALL
SELECT drm.role_principal_id
FROM sys.database_role_members drm
INNER JOIN CTE_Roles CR
ON drm.member_principal_id = CR.role_principal_id
)
SELECT USER_NAME(role_principal_id) RoleName
FROM CTE_Roles
ORDER BY RoleName;
you could join that with the SecurityMap-table to filter out roles like db_owner

Selecting first unique email address in SQL

I have a list of UserIds (which is a uniqueidentifier), Names, email addresses and relationships.
Something like
SELECT UserId, Name, Email, Relationship FROM Users
Sample data would look like:
F87B7702-F0EE-11D3-B288-0000B4A488D3 Peter peter#peter.com Member
ZZZZZZZZ-F0EE-11D3-B288-0000B4A488D2 Joan peter#peter.com Principal
AAAAAAAA-F0EE-11D3-EEEE-0000B4A488D3 Bob bob#bob.com Principal
Relationship can be either be 'Principal' or 'Member', but a principal user can also be a member.
An email address isn't specific to a user (often a member user will have the same email address as a principal user).
I need to ensure that I only select 1 user per email address so that the same person won't be emailed twice. If there are 2 emails for the same user I need to select the principal record.
What's the easiest way to do this, bearing in mind that you can't do a max on a uniqueidentifier field? For the sample data I gave above I would need to return only the second and third record.
I was leaning towards ordering by Relationship, doing a group by and then max but I don't think that's possible.
Output required is UserId, Name, Email.
The ORDER BY in ROW_NUMBER() will allow you to choose how to prioritise your emails. Such as how to deal with an email with no Principle but multiple Members (maybe add , Name ASC so the member with the first alphabetically ordered name gets chosen?)
SELECT
*
FROM
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY email ORDER BY Relationship DESC) AS sequence_id
FROM
yourTable
)
AS sequenced_table
WHERE
sequence_id = 1
I didn't quite understood you, but if you want to get email only once, preffering the principle user this code will do:
select UserId, Name, Email, Relationship
from Table
where Relationship='Principle' or ( Relationship='Member' and Email not in
(
select Email
from Table
where Relationship='Principle'
)
)
This will give you the second and third lines. If there are more conditions- expand your example.