Having two SQL Server related tables, select complete rows of one and partial of other - sql

In MS Access was very easy to acomplish but I'm having troubles with SQL Server
I have this query:
SELECT Organigrama.Item, Organigrama.Id, Organigrama.ParentItem, Rol_Menu.Cod_Rol
FROM Rol_Menu RIGHT JOIN
Organigrama ON Rol_Menu.Cod_Menu = Organigrama.Id
WHERE (Rol_Menu.Cod_Rol = '5')
The purpose is to get all the items of Organigrama and the elements in common with Rol_Menu.Col_Rol appears with 5, the others with Null
I need to fill a menu structure into a treeview
When the user select another Rol just get nodes checked that rol have access to
im determining if in the row the Col_Rol isn't null so the query I need to get
something like this:
Item | Id | ParentItem | Cod_Rol
A | 3 | null | 5
B | 4 | A | 5
C | 5 | A | null
D | 6 | B | 5
E | 7 | C | null
F | 8 | E | null

I think you just need to include the extra restriction in the join criteria rather then the where clause. The criteria are evaluated before the outer join adds the null columns. The where clause is evaluated afterwards, and eliminates the nulls.
select
Organigrama.Item,
Organigrama.Id,
Organigrama.ParentItem,
Rol_Menu.Cod_Rol
from
Rol_Menu
right join
Organigrama
on Rol_Menu.Cod_Menu = Organigrama.Id and
Rol_Menu.Cod_Rol = '5'
either that or add or Rol_Menu.Cod_Rol is null to the end of the where clause.

Related

Only select from tables that are of interest

SQL Server 2016
I have a number of tables
Table A Table B Table C Table D
User | DataA User | DataB User | DataC User | DataD
=========== =========== =================== =============
1 | 10 1 | 'hello' 4 | '2020-01-01' 1 | 0.34
2 | 20 2 | 'world'
3 | 30
So some users have data for A,B,C and/or D.
Table UserEnabled
User | A | B | C | D
=============================
1 | 1 | 1 | 0 | 0
2 | 1 | 1 | 0 | 0
3 | 1 | 0 | 0 | 0
4 | 0 | 0 | 1 | 0
Table UserEnabled indicates whether we are interested in any of the data in the corresponding tables A,B,C and/or D.
Now I want to join those tables on User but I do only want the columns where the UserEnabled table has at least one user with a 1 (ie at least one user enabled). Ideally I only want to join the tables that are enabled and not filter the columns from the disabled tables afterwards.
So as a result for all users I would get
User | DataA | DataB | DataC
===============================
1 | 10 | 'hello' | NULL
2 | 20 | 'world' | NULL
3 | 30 | NULL | NULL
4 | NULL | NULL | '2020-01-01'
No user has D enabled so it does not show up in a query
I was going to come up with a dynamic SQL that's built every time I execute the query depending on the state of UserEnabled but I'm afraid this is going to perform poorly on a huge data set as the execution plan will need to be created every time. I want to dynamically display only the enabled data, not columns with all NULL.
Is there another way?
Usage will be a data sheet that may be generated up to a number of times per minute.
You have no choice but to approach this through dynamic SQL. A select query has a fixed set of columns defined when the query is created. No such thing as "variable" columns.
What can you do? One method is to "play a trick". Store the columns as JSON (or XML) and delete the empty columns.
Another method is to create a view that has the specific logic you need. I think you can maintain this view by altering it in a trigger, based on when data in the enabled table changes. That said, altering the view requires dynamic SQL so the code will not be pretty.
Just because I thought this could be fun.
Example
Declare #Col varchar(max) = ''
Declare #Src varchar(max) = ''
Select #Col = #Col+','+Item+'.[Data'+Item+']'
,#Src = #Src+'Left Join [Table'+Item+'] '+Item+' on U.[User]=['+Item+'].[User] and U.['+Item+']=1'+char(13)
From (
Select Item
From ( Select A=max(A)
,B=max(B)
,C=max(C)
,D=max(D)
From UserEnabled
Where 1=1 --<< Use any Key Inital Filter Condition Here
) A
Unpivot ( value for item in (A,B,C,D)) B
Where Value=1
) A
Declare #SQL varchar(max) = '
Select U.[User]'+#Col+'
From #UserEnabled U
'+#Src
--Print #SQL
Exec(#SQL)
Returns
User DataA DataB DataC
1 10 Hello NULL
2 20 World NULL
3 30 NULL NULL
4 NULL NULL 2020-01-01
The Generated SQL
Select A.[User],A.[DataA],B.[DataB],C.[DataC]
From UserEnabled U
Left Join TableA A on U.[User]=[A].[User] and U.[A]=1
Left Join TableB B on U.[User]=[B].[User] and U.[B]=1
Left Join TableC C on U.[User]=[C].[User] and U.[C]=1
If all the relations are 1:1, you can make one query with
...
FROM u
LEFT JOIN a ON u.id = a.u_id
LEFT JOIN b ON u.id = b.u_id
LEFT JOIN c ON u.id = c.u_id
LEFT JOIN d ON u.id = d.u_id
...
and use display logic on the client to omit the irrelevant columns.
If more than one relation is 1:N, then you'd likely have to do multiple queries anyway to prevent N1xN2 results.

Complex filtering SQL Server query with multiple conditions

I have a SQL Server query in which I am joining several tables together and we are using SQL Server 2012. I have a customer number, customer name, item type, a field for document number (which can be one document number in either of the two tables I will mention; even though there are two fields mentioned later, I do want to combine this into one field using a CASE statement), a document amount, document date, due date, amount remaining to be paid, and a description.
The problem is that for my item type, one table (RM20101), doesn't recognize an item type associated with it because no item types are in that table. The other table (Custom_RecData), recognizes the correct item type for all pertinent document numbers but that tables only has a record of document numbers associated with items--which not all document numbers in the system have.
So here's an example of the trimmed down version:
CUSTNMBR | ItemType | DocumentNumberRM | DocNumCUSTOM | DocAmount
------------+--------------+--------------------+---------------+-----------
12345ABC | NULL | PYMNT01234567 | NULL | - 28.50
12345ABC | TOYS | 9010456778 | 9010456778 | 300.00
12345ABC | NULL | 9010456778 | NULL | 300.00
12345ABC | NULL | 9019888878 | NULL | 47.90
12345ABC | CRAFTS | 9502345671 | 9502345671 | 145.25
12345ABC | NULL | 9502345671 | NULL | 145.25
I named the columns in this example for document number as "RM" to come out of the RM20101 table and "CUSTOM" to come out of the Custom_RecDate table.
So I've tried lots of ways, from CASE statements, to my WHERE clause, to a HAVING clause, to subqueries... I can't figure it out. Here's what I'd like to see:
CUSTNMBR | ItemType | DocumentNumberRM | DocNumCUSTOM | DocAmount
------------+--------------+--------------------+---------------+-----------
12345ABC | NULL | PYMNT01234567 | NULL | - 28.50
12345ABC | TOYS | 9010456778 | 9010456778 | 300.00
12345ABC | NULL | 9019888878 | NULL | 47.90
12345ABC | CRAFTS | 9502345671 | 9502345671 | 145.25
So why did I call this multiple conditions? Well, if you look at the table, I am cutting out items based on the following:
If both the ItemType And DocNumCUSTOM fields are NULL then show it.
If the ItemType IS NOT NULL and both document number fields are NOT NULL, then show it.
If the ItemType IS NULL and DocNumCUSTOM IS NULL, but we've already seen the document number in the RM20101 DocumentNumberRM field (which I suspect would be in a HAVING clause for a COUNT or something), then don't show it a second time.
If I don't find a way to do step 3, I will get the duplicates as shown in my initial example.
I basically do not want duplication. I want to show an item type whether it be NULL (non-existent for that document number in either table) or show it only once if existent in the Custom_RecDate table, which is the only table that has the item type information in it.
Does this make sense? I know it sounds complicated as heck, but hopefully someone can make sense of it all. :)
Thanks!
By the way, here's the important part (my query, albeit very trimmed down for the example):
SELECT DISTINCT
R1.CUSTNMBR,
I.ITMCLSCD AS [ItemType],
R1.DOCNUMBR AS [DocumentNumberRM],
I.DOCNUMBR AS [DocNumCUSTOM],
R1.ORTRXAMT AS [DocAmount]
FROM
RM20101 R1
JOIN
RM40401 R2 ON R2.RMDTYPAL = R1.RMDTYPAL
JOIN
RM00401 R3 ON R3.DOCNUMBR = R1.DOCNUMBR
JOIN
RM00101 R4 ON R4.CUSTNMBR = R1.CUSTNMBR
LEFT OUTER JOIN
SR_ITCMCD C ON C.CUSTNMBR = R1.CUSTNMBR
LEFT OUTER JOIN
AR_Description D ON D.SOPNUM = R1.DOCNUMBR
LEFT OUTER JOIN
Custom_RecData I ON I.ITEMNMBR = C.ITEMNMBR
AND I.DOCNUMBR = R1.DOCNUMBR
Again, I've tried using CASE statements and putting various conditions in my WHERE clause, I just can't figure it out.
Purely based on the sample data, MAX() should do the trick:
SELECT
R1.CUSTNMBR,
MAX(I.ITMCLSCD) AS [ItemType],
R1.DOCNUMBR AS [DocumentNumberRM],
MAX(I.DOCNUMBR) AS [DocNumCUSTOM],
R1.ORTRXAMT AS [DocAmount]
FROM RM20101 R1
JOIN RM40401 R2 ON R2.RMDTYPAL = R1.RMDTYPAL
JOIN RM00401 R3 ON R3.DOCNUMBR = R1.DOCNUMBR
JOIN RM00101 R4 ON R4.CUSTNMBR = R1.CUSTNMBR
LEFT OUTER JOIN SR_ITCMCD C ON C.CUSTNMBR = R1.CUSTNMBR
LEFT OUTER JOIN AR_Description D ON D.SOPNUM = R1.DOCNUMBR
LEFT OUTER JOIN Custom_RecData I ON I.ITEMNMBR = C.ITEMNMBR
AND I.DOCNUMBR = R1.DOCNUMBR
GROUP BY
R1.CUSTNMBR,
R1.DOCNUMBR,
R1.ORTRXAMT
Removed the DISTINCT since GROUP BY will accomplish the same and is needed for MAX()

Access SQL Max-Function

I have a question concerning MS Access queries involving these tables:
tblMIDProcessMain ={ Process_ID,Process_Title,...}
tblMIDProcessVersion = { ProcessVersion_ID, ProcessVersion_FK_Process, ProcessVersion_VersionNo, ProcessVersion_FK_Status, ...}
tblMIDProcessVersionStatus = { ProcessVersionStatus_ID,ProcessVersionStatus_Value }
The tables store different versions of a process description. The "ProcessVersion_VersionNo" field contains an integer providing the version number. Now I would like to get for each process the highest version number thus the current version. If I do the following it kind of works:
SELECT tblMIDProcessMain.Process_Titel
, Max(tblMIDProcessVersion.ProcessVersion_VersionNo) AS CurrentVersion
FROM tblMIDProcessMain
INNER JOIN tblMIDProcessVersion
ON tblMIDProcessMain.Process_ID = tblMIDProcessVersion.ProcessVersion_FK_Process
GROUP BY tblMIDProcessMain.Process_Titel;
The query returns a recordset with each existing process_title and the respective max number of the version field. But as soon as I add other fields like "ProcessVersion_FK_Status" in the Select statement the query stops working.
Any help would be appreciated. Thanks in advance.
Jon
Edit:
To clarify things a little I added a simplified example
Parent-Table:
Process_ID | Process_Title
----------------------------------
1 | "MyProcess"
2 | "YourProcess"
Child-Table:
Version_ID | Version_FK_ProcessID | Version_No | Version_Status
---------------------------------------------------------------------------
1 | 1 | 1 | "New"
2 | 2 | 1 | "Discarded"
3 | 2 | 2 | "Reviewed"
4 | 2 | 3 | "Released"
Intended Result:
Title | Max_Version_No | Status
--------------------------------------------------------
MyProcess | 1 | "New"
YourProcess | 3 | "Released"
Given the example tables you updated your post with, this should work:
select process_title as Title
, max_version.max_version_no
, c.version_status as status
from (parenttable p
inner join (select max(version_id) as max_version_no, version_fk_process_id from childtable group by version_fk_process_id) max_version
on p.process_id = max_version.version_fk_process_id)
inner join childtable c
on max_version.max_version_no = c.version_id and max_version.version_fk_process_id = c.version_fk_process_id
I assume you are adding the new field to the 'Group By" clause? If not, then you either must include in the 'Group By', or you must use one of the operators like "Max" or "First" etc.

SQL: how to make a GROUP BY query considerin' also empty fields

I have to group some data into categories, based on the column "qualifier" that could be 1,2,3,4, or empty.
The problem is that "empty" is not considered into the "group by" categories.
Here's my query:
SELECT m.id, m.name, COUNT (*)
FROM _gialli_g2bff_distinct AS g
INNER JOIN flag.qualifier_flags AS m ON g.qualifier = m.id
GROUP BY m.name, m.id
ORDER BY m.id;
Here's the answer:
1 | "NOT_CONTRIBUTES_TO" | 2
2 | "CONTRIBUTES_TO" | 411
3 | "COLOCALIZES_WITH" | 200
4 | "NOT" | 983
The problem of this answer is that it does not take into account all the elements that have qualifier field EMPTY.
Here's what I would like to have as answer:
1 | "NOT_CONTRIBUTES_TO" | 2
2 | "CONTRIBUTES_TO" | 411
3 | "COLOCALIZES_WITH" | 200
4 | "NOT" | 983
5 | | 1854
How could I modify my query?
Thanks
Your problem is not occuring at the GROUP BY level, but rather in the JOIN. The rows with a NULL qualifier cannot be JOINed and, because you're using INNER JOIN, they fall out of the result set.
Use LEFT OUTER JOIN to see all the rows.

join on three tables? Error in phpMyAdmin

I'm trying to use a join on three tables query I found in another post (post #5 here). When I try to use this in the SQL tab of one of my tables in phpMyAdmin, it gives me an error:
#1066 - Not unique table/alias: 'm'
The exact query I'm trying to use is:
select r.*,m.SkuAbbr, v.VoucherNbr from arrc_RedeemActivity r, arrc_Merchant m, arrc_Voucher v
LEFT OUTER JOIN arrc_Merchant m ON (r.MerchantID = m.MerchantID)
LEFT OUTER JOIN arrc_Voucher v ON (r.VoucherID = v.VoucherID)
I'm not entirely certain it will do what I need it to do or that I'm using the right kind of join (my grasp of SQL is pretty limited at this point), but I was hoping to at least see what it produced.
(What I'm trying to do, if anyone cares to assist, is get all columns from arrc_RedeemActivity, plus SkuAbbr from arrc_Merchant where the merchant IDs match in those two tables, plus VoucherNbr from arrc_Voucher where VoucherIDs match in those two tables.)
Edited to add table samples
Table arrc_RedeemActivity
RedeemID | VoucherID | MerchantID | RedeemAmt
----------------------------------------------
1 | 2 | 3 | 25
2 | 6 | 5 | 50
Table arrc_Merchant
MerchantID | SkuAbbr
---------------------
3 | abc
5 | def
Table arrc_Voucher
VoucherID | VoucherNbr
-----------------------
2 | 12345
6 | 23456
So ideally, what I'd like to get back would be:
RedeemID | VoucherID | MerchantID | RedeemAmt | SkuAbbr | VoucherNbr
-----------------------------------------------------------------------
1 | 2 | 3 | 25 | abc | 12345
2 | 2 | 5 | 50 | def | 23456
The problem was you had duplicate table references - which would work, except for that this included table aliasing.
If you want to only see rows where there are supporting records in both tables, use:
SELECT r.*,
m.SkuAbbr,
v.VoucherNbr
FROM arrc_RedeemActivity r
JOIN arrc_Merchant m ON m.merchantid = r.merchantid
JOIN arrc_Voucher v ON v.voucherid = r.voucherid
This will show NULL for the m and v references that don't have a match based on the JOIN criteria:
SELECT r.*,
m.SkuAbbr,
v.VoucherNbr
FROM arrc_RedeemActivity r
LEFT JOIN arrc_Merchant m ON m.merchantid = r.merchantid
LEFT JOIN arrc_Voucher v ON v.voucherid = r.voucherid