Retrieve records through inner join - sql

I have a schema as per the below:
CREATE TABLE rruser (
id NUMBER(32,0) NOT NULL,
name VARCHAR2(30) NOT NULL,
fullname VARCHAR2(100) NOT NULL,
active_flag CHAR(1) DEFAULT 'N' NOT NULL
)
CREATE TABLE rruser_group (
user_id NUMBER(32,0) NOT NULL,
group_id NUMBER(32,0) NOT NULL
)
CREATE TABLE rrgroup (
id NUMBER(32,0) NOT NULL,
name VARCHAR2(100) NOT NULL,
code VARCHAR2(20) NOT NULL
)
CREATE TABLE rrgroup_permission (
group_id NUMBER(32,0) NOT NULL,
permission_id NUMBER(32,0) NOT NULL
)
CREATE TABLE rrpermission (
id NUMBER(32,0) NOT NULL,
name VARCHAR2(100) NOT NULL,
description VARCHAR2(1000) NOT NULL
)
The connectivity is such that RRUSER is linked to RRGROUP via table RRUSER_GROUP
and RRGROUP further linked with RRPERMISSION via table RRGROUP_PERMISSION.
I have to find out the users whose active flag value is equal to 'Y' within RRUSER; I'm using the below query
SELECT * FROM rruser WHERE ACTIVE_FLAG = 'Y'
I then have to find out the users which have write permission; in the last table, RRPERMISSION, there is a column NAME, which has the write permissions where the value of this column is 'write'. What query could I use to get this information? I know it should be achieved using an INNER JOIN.
Right now, I have tried a query for particular user to know whether he has write permission or not and have found out that he has write permission like this...
SELECT count(ID) FROM rruser WHERE ACTIVE_FLAG = 'Y';
SELECT * FROM rruser WHERE ACTIVE_FLAG = 'Y' AND FULLNAME = 'sss'
SELECT * FROM rruser_group WHERE USER_ID = 1100
SELECT * FROM rrgroup WHERE ID = 113;
SELECT * FROM rrgroup_permission WHERE GROUP_ID = 189 ;
SELECT * FROM rrpermission WHERE ID = 990

Try this:
SELECT ru.* FROM rruser ru
inner join rruser_group rg ON ru.id = rg.user_id
inner join rrgroup_permission rgp ON rg.group_id = rgp.group_id
inner join rrpermission rp ON rgp.permission_id = rp.id WHERE ru.ACTIVE_FLAG='Y' AND rp.name='write'

Related

Specific SQL query for invariant locales

I have a table with the same descriptions on many languages for one object. I need to construct a select query to get one needed description having user locale, invariant locale, and if any of the locales will not match with data in the table get one random record.
+---+----+--------+--------+-------------+
| | id | locale | object | description |
+---+----+--------+--------+-------------+
| 1 | 1 | it | cat | gatto |
| 2 | 2 | pl | cat | kot |
| 3 | 3 | de | cat | Katze |
| 4 | 4 | en | cat | cat |
+---+----+--------+--------+-------------+
If user locale is 'it' and the default is 'en', must return -> gatto;
If user locale is 'ru' and the default is 'en', must return -> cat;
If user locale is 'ru' and the default is 'po', must return -> any record;
Do you know any variant of query which can do that thing?
You could use coalesce() and conditional aggregation as follows:
select
coalesce(
max(case when locale = :user_locale then description end),
max(case when locale = :default_locale then description end)
)
from mytable
where object = 'cat'
Where :user_locale is the parameter that contains the user locale and :default_locale contains the default locale.
First thing you should do is split the table in three entity to avoid redondance in the column "object" AND to avoid violating 3rd normal form (with column "object" and "description" being in functional dependence with locale wich is not the key).
So you will have the following table :
Locale(id_locale, locale)
-->id_locale is primary key
Object(id_object, code)
--> id_object is primary key
Locale_Tran(id_locale,id_object, description)
--> (id_locale, id_object) is composed primary key
--> id_locale is foreign key references on Locale(id_locale)
--> id_object is foreign key references on Object(id_object)
(I choose the name for the table but of course you can adapt it to your actual model)
CREATE DATABASE TEST
USE TEST
CREATE TABLE [LOCALE]
(
id_locale INT IDENTITY(1,1),
locale CHAR(2),
CONSTRAINT pk_locale PRIMARY KEY(id_locale)
);
CREATE TABLE [USER]
(
id INT IDENTITY(1,1),
locale INT NOT NULL,
CONSTRAINT fk_user_locale FOREIGN KEY(locale) REFERENCES LOCALE(id_locale)
);
CREATE TABLE [OBJECT]
(
id_object INT IDENTITY(1,1),
code VARCHAR(100),
CONSTRAINT pk_object PRIMARY KEY(id_object)
);
CREATE TABLE [LOCALE_TRAN]
(
id_locale INT,
id_object INT,
[description] VARCHAR(200) NOT NULL,
CONSTRAINT fk_tran_locale FOREIGN KEY(id_locale) REFERENCES [LOCALE](id_locale),
CONSTRAINT fk_locale_object FOREIGN KEY(id_object) REFERENCES [OBJECT](id_object),
CONSTRAINT pk_locale_tran PRIMARY KEY(id_locale, id_object)
)
INSERT INTO LOCALE
(locale)
VALUES
('IT'),
('FR'),
('PL'),
('DE'),
('EN'),
('RU');
INSERT INTO [USER]
(locale)
VALUES
(2),
(3),
(4),
(5),
(6),
(6),
(1)
INSERT INTO [OBJECT]
(code)
VALUES
('CAT'),
('FISH');
INSERT INTO [LOCALE_TRAN]
VALUES
(1,1,'gatto'),
(2,1,'chat'),
(3,1,'kot'),
(4,1,'katze'),
(5,1,'cat'),
(1,2,'pesce')
Here is the query :
DROP PROCEDURE IF EXISTS dbo.getTran
GO;
CREATE PROCEDURE dbo.getTran
(
#userLocale CHAR(2),
#defaultLocale CHAR(2),
#objectId INT
)
AS
SELECT #userLocale = (SELECT id_locale FROM LOCALE l WHERE l.locale = #userLocale);
SELECT #defaultLocale = (SELECT id_locale FROM LOCALE l WHERE l.locale = #defaultLocale);
SELECT
coalesce(
max(case when id_locale = #userLocale then [description] end),
max(case when id_locale = #defaultLocale then [description] end)
)
FROM
(
SELECT lt.id_locale, lt.[description]
FROM [LOCALE_TRAN] lt WHERE lt.id_object = #objectId
) AS t
GO;
EXEC dbo.getTran 'FR','EN', '1'
I don't really found how to have the random locale and i didn't really understand why you wanna do that... But i will try.
Apply your conditions inside CTEs with the use of NOT EXISTS:
with
cte1 as (select description from tablename where locale = ParamLocale),
cte2 as (
select t.description from tablename t
where t.locale = ParamDefault
and not exists (select 1 from cte1)
),
cte3 as (
select * from cte1
union all
select * from cte2
)
select description from cte3
union all
select * from (
select description from tablename
where not exists (select 1 from cte3)
order by random() limit 1
)
Change ParamLocale and ParamDefault with your parameters.
See the demo.

Looping through groups of records

SQL Server 2014, I have a table with a number of rows for example 15, 5 have a groupid column of 736881 and 10 have a group id column 3084235. What I want to do is process each group of records in turn and load the results in to a table.
I have written the code to do this but I think I am not setting the loopcounter incorrectly set as I keep getting the groupid of records 736881 loaded twice.
I cant't currently post the test data due to containing personal information but if the mistake is not obvious I will try and create some dummy data.
SELECT #LoopCounter = min(rowfilter) , #maxrowfilter = max(rowfilter)
FROM peops6
WHILE ( #LoopCounter IS NOT NULL
AND #LoopCounter <= #maxrowfilter)
begin
declare #customer_dist as Table (
[id] [int] NOT NULL,
[First_Name] [varchar](50) NULL,
[Last_Name] [varchar](50) NULL,
[DoB] [date] NULL,
[post_code] [varchar](50) NULL,
[mobile] [varchar](50) NULL,
[Email] [varchar](100) NULL );
INSERT INTO #customer_dist (id, First_Name, Last_Name, DoB, post_code, mobile, Email)
select id, first_name, last_name, dob, postcode, mobile_phone, email from peops6 where rowfilter = #LoopCounter
insert into results
SELECT result.* ,
[dbo].GetPercentageOfTwoStringMatching(result.DoB, d.DoB) [DOB%match] ,
[dbo].GetPercentageOfTwoStringMatching(result.post_code, d.post_code) [post_code%match] ,
[dbo].GetPercentageOfTwoStringMatching(result.mobile, d.mobile) [mobile%match] ,
[dbo].GetPercentageOfTwoStringMatching(result.Email, d.Email) [email%match]
FROM ( SELECT ( SELECT MIN(id)
FROM #customer_dist AS sq
WHERE sq.First_Name = cd.First_Name
AND sq.Last_Name = cd.Last_Name
AND ( sq.DoB = cd.DoB
OR sq.mobile = cd.mobile
OR sq.Email = cd.Email
OR sq.post_code = cd.post_code )) nid ,
*
FROM #customer_dist AS cd ) AS result
INNER JOIN #customer_dist d ON result.nid = d.id order by 1, 2 asc;
SELECT #LoopCounter = min(rowfilter) FROM peops6
WHERE rowfilter > #LoopCounter
end
You need to truncate your table variable (#customer_dist) at the end of the loop:
....
-- Add this
TRUNCATE TABLE #customer_dist
SELECT #LoopCounter = min(rowfilter) FROM peops6
WHERE rowfilter > #LoopCounter
end
See: https://social.msdn.microsoft.com/Forums/sqlserver/en-US/42ef20dc-7ad8-44f7-b676-a4596fc0d593/declaring-a-table-variable-inside-a-loop-does-not-delete-the-previous-data?forum=transactsql
I am not sure you need a LOOP like using a SQL Cursor to fulfill this task
Please check following SQL statement where I used multiple CTE expressions
with customer_dist as (
select
rowfilter,
id, first_name, last_name, dob, postcode, mobile_phone, email
from peops6
), result as (
SELECT
(
SELECT
MIN(id)
FROM customer_dist AS sq
WHERE
sq.rowfilter = cd.rowfilter
AND sq.First_Name = cd.First_Name
AND sq.Last_Name = cd.Last_Name
AND (sq.DoB = cd.DoB OR sq.mobile_phone = cd.mobile_phone OR sq.Email = cd.Email OR sq.postcode = cd.postcode )
) nid,
*
FROM customer_dist AS cd
)
SELECT
result.* ,
[dbo].edit_distance(result.DoB, d.DoB) [DOB%match] ,
[dbo].edit_distance(result.postcode, d.postcode) [post_code%match] ,
[dbo].edit_distance(result.mobile_phone, d.mobile_phone) [mobile%match] ,
[dbo].edit_distance(result.Email, d.Email) [email%match]
FROM result
INNER JOIN customer_dist d
ON result.nid = d.id
order by 1, 2 asc;
Please note, I used my fuzzy string matching Levenshtein Distance Algorithm in this sample instead of your function
And the outcome is as follows
Only you need to add the INSERT statement just before the last SELECT statement
Hope it is useful

Get or else insert in PostgreSQL

I have a table of objects with columns for a number of properties and a column with a unique, SERIAL identifier.
for example:
CREATE TABLE person(
id SERIAL NOT NULL,
name VARCHAR(16) NOT NULL,
age INT NOT NULL,
can_drive BOOL NOT NULL
)
Now, is it possible to write a single query, which checks to see if an entry ( eg: Fred, 27, true) is in the table, and if it is, returns the id, else inserts the entry and returns the new id?
try:
t=# with i as (
insert into person (name,age,can_drive)
select 'Fred',27,true
where not exists (
select 0 from person where name = 'Fred' and age= 27 and can_drive
)
returning id
)
select * from i
union all
select id from person where name = 'Fred' and age= 27 and can_drive;
id
----
1
(1 row)

Converting strange SQL Server JOIN syntax to MySQL syntax

I have a SQL Server query that I am attempting to port to MySQL, but the JOIN syntax is something that I have never seen used before. The query is from a view designed to measure procedure code usage. What the heck is going on with the JOIN syntax just past T.PatID = P.ID, and the third LEFT OUTER JOIN, and what equivalent syntax can we use in MySQL? It does not like this JOIN syntax at all (disregard the ISNULL and CONVERT SQL Server specific syntax)
SELECT
T.Code
, P.LastName
, P.FirstName
, T.TranDate
, CD.DaysUnits
, T.TranAmt
, TD.FullName AS Provider
, ISNULL(TD.ID, ISNULL(AD.ID, PD.ID)) AS DoctorID
FROM
dbo.Doctors AS PD
INNER JOIN
dbo.Transactions AS T
INNER JOIN
dbo.Patients AS P
ON
T.PatID = P.ID
ON
PD.ID = P.DoctorID
LEFT OUTER JOIN
dbo.Doctors AS TD
ON
T.DoctorID = TD.ID
LEFT OUTER JOIN
dbo.Doctors AS AD
LEFT OUTER JOIN
dbo.Appointments
ON
AD.ID = dbo.Appointments.DoctorID
AND CONVERT(varchar(20), dbo.Appointments.ScheduleDateTime, 8) <> '00:00:00'
ON
T.ApptID = dbo.Appointments.ID
LEFT OUTER JOIN
dbo.ChargeDetails AS CD
ON
T.ID = CD.ChargeTranID
WHERE
(
T.Code IS NOT NULL
)
The SHOW CREATE TABLE are as follows
CREATE TABLE Doctors
(
ID int(10) NOT NULL PRIMARY KEY
, FullName varchar(50) DEFAULT NULL
)
CREATE TABLE Patients
(
LName varchar(50) DEFAULT NULL
, FName varchar(50) DEFAULT NULL
, ID int(10) NOT NULL PRIMARY KEY
)
CREATE TABLE Transactions
(
TranType varchar(2) DEFAULT NULL
, Code varchar(100) DEFAULT NULL
, TranSubType varchar(2) DEFAULT NULL
, Description varchar(2000) DEFAULT NULL
, TranDate datetime
, PatID int(10) DEFAULT NULL
, ID int(10) NOT NULL PRIMARY KEY
, TranAmt decimal(19,4) DEFAULT NULL
, ApptID int(10) DEFAULT NULL
, DoctorID int(10) DEFAULT NULL
)
CREATE TABLE ChargeDetails
(
DaysUnits varchar(50) DEFAULT NULL
-- DaysUnits is just an int ranging from 1 to 2
, ChargeTranID int(10) NOT NULL PRIMARY KEY
)
CREATE TABLE Appointments
(
DoctorID int(10) DEFAULT NULL
, PatientID int(10) DEFAULT NULL
, ScheduleDateTime datetime DEFAULT NULL
, ID int(10) NOT NULL PRIMARY KEY
)
Thank you in advance for your help.
Here is a similar (and simplified) query using the same structure as the first query. The second query moves the joins around to make things easier to read.
set nocount on;
use tempdb;
go
declare #doc table (id int not null);
declare #tran table (id int not null, patid int not null);
declare #patients table (id int not null, docid int not null);
insert #doc (id) values (1);
insert #patients (id, docid) values (25, 1);
insert #tran (id, patid) values (100, 25)
select *
from #doc as pd
inner join #tran as t
inner join #patients as p
on t.patid = p.id
on pd.id = p.docid;
select *
from #tran as t
inner join #patients as p
on t.patid = p.id
inner join #doc as pd
on pd.id = p.docid;
Other things look strange. I don't see a need to join to appointments but I'm not going to spend a lot of time to figure out the logic and the schema. The convert usage seems like a bad way to check for null - unless there is a special "flag" datetime value that is used as the equivalent to null. Again, you need to understand the query, the goal of the query, the schema on which it is based, and how the tables are populated. Quite frankly, this code raises concerns about the quality of the entire system.
.

SQL - Efficient versioning of DNS records

So far I have come up with this solution that needs further refinement (big thanks to #postgresql on freenode).
The problem I am trying to overcome is an efficient way of storing DNS records whilst maintaining some sort of history. The issue I am currently having is with the wCTE which is inserting new records and deleting old records correctly. It isn't, however, readding records. The wCTE is:
WITH deltas AS (
SELECT o, n FROM (
SELECT id, name, domain_id, class_id, addr FROM record WHERE tld_id = $1
) AS o FULL OUTER JOIN record_temp n
ON (
o.name = n.name AND
o.domain_id = n.domain_id AND
o.class_id = n.class_id AND
o.addr = n.addr
)
WHERE (o.name, o.domain_id, o.class_id, o.addr)
IS DISTINCT FROM (n.name, n.domain_id, n.class_id, n.addr)
), mark_dead AS (
UPDATE record SET alive = FALSE
WHERE id IN (
SELECT (o).id FROM deltas WHERE (o).id IS NOT NULL
) RETURNING *
)
INSERT INTO record (name, domain_id, tld_id, class_id, addr)
SELECT (n).name, (n).domain_id, (n).tld_id, (n).class_id, (n).addr
FROM deltas WHERE
(n).name IS NOT NULL AND
(n).domain_id IS NOT NULL AND
(n).tld_id IS NOT NULL AND
(n).class_id IS NOT NULL AND
(n).addr IS NOT NULL
;
The o result has all the old records that do not exist in record_temp, n has all the records that are new and need to be inserted. I expect I need to add another join which pulls in (an inner join?) results that exist on both tables (which if marked as dead, need to be marked as alive).
The rest of the schema for reference is:
CREATE TABLE record (
id SERIAL,
name VARCHAR(255),
domain_id INT,
tld_id INT,
class_id INT,
addr INET,
alive BOOLEAN DEFAULT TRUE,
PRIMARY KEY (id),
CONSTRAINT fk1 FOREIGN KEY (domain_id) REFERENCES domain (id) MATCH SIMPLE,
CONSTRAINT fk2 FOREIGN KEY (tld_id) REFERENCES tld (id) MATCH SIMPLE,
UNIQUE(name, domain_id, class_id, addr)
);
CREATE TABLE record_history (
id SERIAL,
record_id INT,
history_type record_history_type,
stamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk1 FOREIGN KEY (record_id) REFERENCES record (id) MATCH SIMPLE,
PRIMARY KEY(id)
);
CREATE TEMP TABLE record_temp (
name VARCHAR(255),
domain_id INT,
tld_id INT,
class_id INT,
addr INET,
UNIQUE(name, domain_id, class_id, addr)
)
ON COMMIT DROP;
record_history is populated using functions and triggers and is populating how I expect it to, below are these triggers:
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO record_history (record_id, history_type) VALUES (NEW.id, 'added');
RETURN NEW;
END;
$$ language 'plpgsql';
RETURNS TRIGGER AS $$
BEGIN
IF NEW.alive = OLD.alive THEN
RETURN NEW;
END IF;
IF NEW.alive THEN
INSERT INTO record_history (record_id, history_type) VALUES (NEW.id, 'added');
END IF;
IF NOT NEW.alive THEN
INSERT INTO record_history (record_id, history_type) VALUES (NEW.id, 'deleted');
END IF;
RETURN NEW;
END;
$$ language 'plpgsql';
ON record FOR EACH ROW EXECUTE PROCEDURE
add_insert_record_history();
ON record FOR EACH ROW EXECUTE PROCEDURE
add_update_record_history();
I seem to have it working how I want with the following query, which I feel is incredibly unoptimized:
WITH deltas AS (
SELECT o, n FROM (
SELECT id, name, domain_id, class_id, addr FROM record WHERE tld_id = $1
) AS o FULL OUTER JOIN record_temp n
ON (
o.name = n.name AND
o.domain_id = n.domain_id AND
o.class_id = n.class_id AND
o.addr = n.addr
)
WHERE (o.name, o.domain_id, o.class_id, o.addr)
IS DISTINCT FROM (n.name, n.domain_id, n.class_id, n.addr)
), mark_dead AS (
UPDATE record SET alive = FALSE
WHERE id IN (
SELECT (o).id FROM deltas WHERE (o).id IS NOT NULL
) RETURNING *
), mark_alive AS (
UPDATE record SET alive = TRUE
WHERE alive = FALSE AND id IN (
SELECT id FROM (
SELECT id, name, domain_id, class_id, addr FROM record WHERE tld_id = $1
) AS o INNER JOIN record_temp n
ON (
o.name = n.name AND
o.domain_id = n.domain_id AND
o.class_id = n.class_id AND
o.addr = n.addr
)
) RETURNING *
)
INSERT INTO record (name, domain_id, tld_id, class_id, addr)
SELECT (n).name, (n).domain_id, (n).tld_id, (n).class_id, (n).addr
FROM deltas WHERE
(n).name IS NOT NULL AND
(n).domain_id IS NOT NULL AND
(n).tld_id IS NOT NULL AND
(n).class_id IS NOT NULL AND
(n).addr IS NOT NULL
;