How to deal with two tables with huge data? - sql

I'm facing severe issue with one table having 2 crores of data and other table having service type data which is joining with huge data table
.
Already I have created Clustered index ,Primary key and NonClustered with covering indexes .I have modifed Varchar columns to Char for space purpose I have used all filter conditions
My table looks like :
(2 crores data)
CREATE TABLEA (
ApplicationNO Varchar(25)PK,
Service_type VARCHAR(10),
service_id VARCHAR(10),
ISActive Char(1))
(300 records)
CREATE TABLEB(
service_name Varchar(25),
service_id VARCHAR(10),
service_type VARCHAR(10),
Department_id Char(3))
My Query :
Select A.Servicename,
Count(A.ApplicationNo),
sum(case when p.IS_Active='Y' then 1 else 0 end )as'Complted',
sum(case when p.IS_Active='N' then 1 else 0 end )as'InComplete'
from TableA A INNER JOIN TableB B(Service Data)
ON A.ServiceId = B.ServiceId AND A.ServiceType = B.ServiceType
Where DateFilters fromDate and ToDate AND S.ISActive = 'Y'
After creating indexes still it is taking lot of time I have tried every other way.
Please suggest me possible way.

Use this Code:
EquateA.Service_Id = B.Service_id instead of A.ServiceId = B.ServiceName
As service name not match with Service id
Use this code:
Select A.Servicename,
Count(A.ApplicationNo),
sum(case when p.IS_Active='Y' then 1 else 0 end )as'Complted',
sum(case when p.IS_Active='N' then 1 else 0 end )as'InComplete'
from TableA A INNER JOIN TableB B(Service Data)
ON A.ServiceId = B.Service_id AND A.ServiceType = B.ServiceType
Where DateFilters fromDate and ToDate AND S.ISActive = 'Y'

Related

Solved: Multiplying the result of a SUM() on one table by the result of a COUNT() from another table

I'm trying to multiply the sum computed in one table by the count of values found in another table linked to it.
I have a table with entries like this:
Table_A
Id
SubId
Start
Stop
For each Id there can be several SubId. The Id is linked through a whole series of other tables to a second table which basically looks like this:
Table_B
Id
Val
Id in Table_B can occur from one to multiple times.
The idea is that I want to mulitply the result of the sum of time spans found in Table_A for each SubId by the count of distinct Val found in Table_B by linking it through the Id.
I already have the sum of time spans from Table_A like this (I'm no developper so it might be very shoddy code, I apologize in advance):
SELECT SUM(duration) FROM (
SELECT (mod_stop - mod_start) AS duration FROM (
SELECT
(CASE
WHEN date_start < '#begin' THEN '#begin'
ELSE date_start
END) AS mod_start,
(CASE
WHEN date_stop > '#end' THEN '#end'
WHEN date_stop = '0' THEN '#end'
ELSE date_stop
END) AS mod_stop
FROM table_a
WHERE (
state = 'Launching' OR
state = 'Running' OR
state = 'Finishing'
)
AND table_a_id IN (
SELECT
DISTINCT(table_a_id)
FROM table_a
WHERE
(to_timestamp(date_start), to_timestamp(date_stop)) OVERLAPS (to_timestamp('#begin'), to_timestamp('#end'))
AND
(state = 'Running' OR state = 'Launching' OR state = 'Finishing')
AND
NOT date_stop = '0'
)
) AS t
) AS d
;
This works but I now need to multiply each duration by the number of Val associated to it's Id and I can't work out how to do this.
I thought having another AND ... IN clause in the WHERE with all the table linking mechanisms returning the individual Val count would do it but the query does not return anything despite running for over an hour whereas without it it returns in approximately ten minutes (there is no index on date_* and Table_A is several tens of million records long which explains why it is so slow) so I fear I might have got something wrong.
Is there a way to do this? Thanks!
Thanks to the comments posted and what I managed to find I've cobbled a solution together. It might be crude but it works. I build a temp table whose values I use to build a CTE. Feel free to comment if you think this can be done better.
BEGIN;
CREATE TEMP TABLE my_jsl ON COMMIT DROP AS
SELECT job_id, job_state, mod_start, mod_stop, duration FROM (
SELECT job_id, job_state, mod_start, mod_stop, mod_stop - mod_start duration FROM (
SELECT job_id, job_state,
(CASE
WHEN date_start < :my_begin THEN :my_begin
ELSE date_start
END) AS mod_start,
(CASE
WHEN date_stop > :my_end THEN :my_end
WHEN date_stop = '0' THEN :my_end
ELSE date_stop
END) AS mod_stop
FROM job_state_logs
WHERE (
job_state = 'Launching' OR
job_state = 'Running' OR
job_state = 'Finishing'
)
AND job_state_log_id IN (
SELECT
DISTINCT(job_state_log_id)
FROM job_state_logs
WHERE
(to_timestamp(date_start), to_timestamp(date_stop)) OVERLAPS (to_timestamp(:my_begin), to_timestamp(:my_end))
AND
(job_state = 'Running' OR job_state = 'Launching' OR job_state = 'Finishing')
AND
NOT date_stop = '0'
)
) AS job_temp_log
) AS job_log
;
WITH my_jr AS (
SELECT COUNT(a.resource_id) jrc, j.job_id
FROM assigned_resources a
INNER JOIN moldable_job_descriptions m ON m.moldable_id = a.moldable_job_id
INNER JOIN jobs j ON j.assigned_moldable_job = m.moldable_id
WHERE j.job_id IN (
SELECT DISTINCT(job_id) FROM my_jsl
)
GROUP BY j.job_id
),
my_surf AS (
SELECT my_jr.job_id, my_jsl.duration, my_jr.jrc, (my_jsl.duration * my_jr.jrc) AS surface
FROM my_jr, my_jsl
WHERE my_jr.job_id = my_jsl.job_id
)
SELECT SUM(surface) job_surface FROM my_surf
;
COMMIT;
;

Updating a table using Case statements in SQL

I am trying to add a 0, 1, or null to a column in a specific category where a relativepersonid of a person has a diagdate up to a person's servicedate. Here are my tables:
DROP TABLE ICDCodes_w;
GO
CREATE TABLE ICDCodes_w
(
AnxietyDisorder VARCHAR(6),
DepressiveDisorder VARCHAR(6),
PTSD VARCHAR(6)
);
INSERT INTO ICDCodes_w
(
AnxietyDisorder,
DepressiveDisorder,
PTSD
)
VALUES
('293.84', '296.2', '309.81'),
('300', '296.21', 'F43.1'),
('305.42', 'F11.28', 'F31.76'),
('305.81', 'F43.8', 'F31.78'),
('F40.00', 'F43.10', '305.52');
GO
DROP TABLE DiagHX_w;
GO
CREATE TABLE DiagHX_w
(
ArchiveID VARCHAR(10),
RelativePersonID VARCHAR(10),
ICDCode VARCHAR(6),
DiagDate DATE
);
INSERT INTO DiagHX_w
(
ArchiveID,
RelativePersonID,
ICDCode,
DiagDate
)
VALUES
('1275741', '754241', '293.84', '1989-01-03'),
('2154872', '754241', '293.84', '1995-04-07'),
('4587215', '754241', '998.4', '1999-12-07'),
('4588775', '711121', 'F11.28', '2001-02-07'),
('3545455', '711121', NULL, NULL),
('9876352', '323668', '400.02', '1988-04-09'),
('3211514', '112101', 'F31.78', '2005-09-09'),
('3254548', '686967', 'F40.00', '1999-12-31'),
('4411144', '686967', '305.52', '2000-01-01'),
('6548785', '99999999','F40.00', '2000-02-03');
GO
DROP TABLE PatientFlags_w;
GO
CREATE TABLE PatientFlags_w
(
PersonID VARCHAR(10),
RelativePersonID VARCHAR(10),
AnxietyDisorder VARCHAR(2),
DepressiveDisorder VARCHAR(2),
PTSD VARCHAR(2),
);
INSERT INTO PatientFlags_w
(
PersonID,
RelativePersonID
)
VALUES
('99999999', '754241'),
('88888888', '754241'),
('77777777', '754241'),
('66666666', '711121'),
('55555555', '711121'),
('44444444', '323668'),
('33333333', '112101'),
('22222222', '686967'),
('11111111', '686967'),
('32151111', '887878'),
('78746954', '771125'),
('54621333', '333114'),
('55648888', '333114');
GO
DROP TABLE Person_w;
GO
CREATE TABLE Person_w
(
PersonID VARCHAR(10),
ServiceDate date
);
INSERT INTO Person_w
(
PersonID,
ServiceDate
)
VALUES
('99999999', '2000-12-31'),
('88888888', '2000-11-01'),
('69876541', '2000-09-04'),
('66666666', '2000-01-15'),
('55555555', '2000-07-22'),
('44444444', '2000-07-20'),
('65498711', '2000-11-17'),
('22222222', '2000-09-02'),
('11111111', '2000-02-04'),
('32151111', '2000-02-17'),
('78746954', '2000-03-29'),
('54621333', '2000-08-22'),
('55648888', '2000-10-20');
Here is my update statement:
UPDATE a
SET AnxietyDisorder = CASE
WHEN ICDCode IN
(
SELECT AnxietyDisorder FROM
Project..ICDCodes_w
) THEN
1
ELSE
0
END,
DepressiveDisorder = CASE
WHEN ICDCode IN
(
SELECT DepressiveDisorder FROM
Project..ICDCodes_w
) THEN
1
ELSE
0
END,
PTSD = CASE
WHEN ICDCode IN
(
SELECT PTSD FROM Project..ICDCodes_w
) THEN
1
ELSE
0
END
FROM PatientFlags_w a
JOIN DiagHX_w b
ON a.relativepersonid = b.RelativePersonID
JOIN Person_w p
ON a.personid = p.PersonID
WHERE diagdate <= p.servicedate;
This works on some values, but there are some that don't get updated. I know the issue is with my case statement and probably a join issue. What is a better way to write this? Here is an example query I used to check. The PTSD column should have a 1.
SELECT * FROM project..patientflags_w a
JOIN project..diaghx_w b
ON a.relativepersonid = b.RelativePersonID
JOIN project..person_w p
ON a.personid = p.personid
WHERE b.icdcode IN (SELECT PTSD FROM Project..ICDCodes_w)
AND b.diagdate <= p.servicedate
I did ask this question the other day, but my sample tables were all messed up, so I've verified that they work this time.
At first glance, the problem with your query is that you update the target (PatientFlags_w) multiple times: once for each flag. In some cases you seem to be ending up with the correct result, but its just by luck.
It's hard to tell if you want one row per person in the flag table, or one row per flag.
Can you review these queries and let us know if they are close to your desired results:
-- If you want one row per Person:
select RelativePersonID,
[AnxietyDisorder] = max(case when c.AnxietyDisorder is not null then 1 else 0 end),
[DepressiveDisorder] = max(case when c.DepressiveDisorder is not null then 1 else 0 end),
[PTSD] = max(case when c.PTSD is not null then 1 else 0 end)
from DiagHX_w d
left
join ICDCodes_w c on d.ICDCode in (c.AnxietyDisorder, c.DepressiveDisorder, c.PTSD)
group
by RelativePersonID;
-- If you want one row per Flag:
select RelativePersonID,
d.ICDCode,
[AnxietyDisorder] = case when c.AnxietyDisorder is not null then 1 else 0 end,
[DepressiveDisorder] = case when c.DepressiveDisorder is not null then 1 else 0 end,
[PTSD] = case when c.PTSD is not null then 1 else 0 end
from DiagHX_w d
left
join ICDCodes_w c on d.ICDCode in (c.AnxietyDisorder, c.DepressiveDisorder, c.PTSD);
If the diagnoses are not related to each other (I assumed since they are in the same table), you might want this instead:
select RelativePersonID,
[AnxietyDisorder] = max(case when c.AnxietyDisorder = d.ICDCode then 1 else 0 end),
[DepressiveDisorder] = max(case when c.DepressiveDisorder = d.ICDCode then 1 else 0 end),
[PTSD] = max(case when c.PTSD = d.ICDCode then 1 else 0 end)
from DiagHX_w d
left
join ICDCodes_w c on d.ICDCode in (c.AnxietyDisorder, c.DepressiveDisorder, c.PTSD)
group
by RelativePersonID;

TSQL Select only the highest credentail

I have a query that is returning multiple line for a single service because an individual may have multiple credentials. In the medical field you retain several credentials but for simplicity sake I will use just standard credentials Phd, MA, MS, BA, BS, AS
I need to know the simplest way to ignore rows where Z_ServiceLedger.clientvisit_id has any Credentials.credentials lower in the hierarchy. So if an employee does a service and he has a Phd and a MA only return the lines for Phd and if he has a Phd an Ma and a BA only return the lines for phd. We have around 50 credentials so if I use CASE for each credential you can see how mess that will get an I'm hoping there is a better way to avoid that.
Here is my current query:
SELECT DISTINCT
SUM(CASE WHEN v.non_billable = 0 THEN v.duration ELSE 0 END) / 60 AS billable_hours,
SUM(CASE WHEN (v.non_billable = 0 AND Z_ServiceLedger.payer_id = 63) THEN v.duration ELSE 0 END) / 60 AS billable_mro_hours,
Credentials.credentials
FROM
Z_ServiceLedger
INNER JOIN
ClientVisit v ON Z_ServiceLedger.clientvisit_id = v.clientvisit_id
LEFT JOIN
Employees ON v.emp_id = Employees.emp_id
LEFT JOIN
EmployeeCredential ON Employees.emp_id = EmployeeCredential.emp_id
LEFT JOIN
Credentials ON Credentials.credential_id = EmployeeCredential.credential_id
WHERE
v.rev_timein <= CASE
WHEN EmployeeCredential.end_date IS NOT NULL
THEN EmployeeCredential.end_date
ELSE GETDATE()
END
AND v.rev_timein >= #param1
AND v.rev_timein < DateAdd(d, 1, #param2)
AND Z_ServiceLedger.amount > 0
AND v.splitprimary_clientvisit_id IS NULL
AND v.gcode_primary_clientvisit_id IS NULL
AND v.non_billable = 0
AND v.non_billable = 'FALSE'
AND v.duration / 60 > 0
AND Z_ServiceLedger.action_type NOT IN ('SERVICE RATE CHANGE', 'CLIENT STATEMENT')
AND (EmployeeCredential.is_primary IS NULL OR EmployeeCredential.is_primary != 'False')
AND v.client_id != '331771 '
GROUP BY
Credentials.credentials,
v.non_billable
ORDER BY
Credentials.credentials
Some aliases and formatting really shed some light on some major logical flaws here. You have at least two predicates in your where clause that logically turn a left join into an inner join. This is total shot in the dark since from both of your questions today we don't have anything to actually work with for tables or sample data.
The biggest concern though is your where clause is trying to get rows v.non_billable = 0 and where it equals 'FALSE'. It can't be both.
Select sum(Case When v.non_billable = 0 Then v.duration Else 0 End) / 60 As billable_hours
, sum(Case When (v.non_billable = 0 And sl.payer_id = 63) Then v.duration Else 0 End) / 60 As billable_mro_hours
, c.credentials
From Z_ServiceLedger sl
Inner Join ClientVisit v On sl.clientvisit_id = v.clientvisit_id
Left Join Employees e On v.emp_id = e.emp_id
Left Join EmployeeCredential ec On e.emp_id = ec.emp_id
--if you leave these predicates in the where clause you have turned your left join into an inner join.
AND v.rev_timein <= isnull(ec.end_date, GetDate())
and (ec.is_primary Is Null Or ec.is_primary != 'False')
Left Join Credentials c On c.credential_id = ec.credential_id
Where v.rev_timein >= #param1
And v.rev_timein < DateAdd(day, 1, #param2)
And v.splitprimary_clientvisit_id Is Null
And v.gcode_primary_clientvisit_id Is Null
--you need to pick one value for v.non_billable. It can't be both 0 and 'FALSE' at the same time.
And v.non_billable = 0
And v.non_billable = 'FALSE'
--And v.duration / 60 > 0
and v.duration > 60 --this is the same thing and is SARGable
And sl.amount > 0
And sl.action_type NOT IN ('SERVICE RATE CHANGE', 'CLIENT STATEMENT')
And v.client_id != '331771 '
Group By c.credentials
, v.non_billable
Order By c.credentials
EDIT: Modified query to add a CTE to calculate the credential_rank, using a FROM (VALUES (...)) table-value-constructor syntax. This works in SQL 2008+. (https://learn.microsoft.com/en-us/sql/t-sql/queries/table-value-constructor-transact-sql?view=sql-server-2017)
SQL Fiddle
First, I'll build out a very simple piece of data.
SETUP:
CREATE TABLE Employees ( emp_id int, emp_name varchar(20) ) ;
INSERT INTO Employees (emp_id, emp_name)
VALUES (1,'Jay'),(2,'Bob')
;
CREATE TABLE Credentials ( credential_id int, credentials varchar(20), credential_rank int ) ;
INSERT INTO Credentials (credential_id, credentials, credential_rank)
VALUES (1,'BA',3),(2,'MA',2),(3,'PhD',1)
;
CREATE TABLE EmployeeCredential (emp_id int, credential_id int, is_primary bit, end_date date )
INSERT INTO EmployeeCredential (emp_id, credential_id, is_primary, end_date)
VALUES
( 1,2,null,'20200101' )
, ( 1,3,0,'20200101' ) /* NON-PRIMARY */
, ( 1,1,1,'20100101' ) /* EXPIRED CRED */
, ( 2,3,null,'20200101' )
, ( 2,3,1,'20200101' )
;
CREATE TABLE z_ServiceLedger ( payer_id int, clientvisit_id int, amount int, action_type varchar(50) ) ;
INSERT INTO z_ServiceLedger ( payer_id, clientvisit_id, amount, action_type )
VALUES (63,1,10,'XXXXX'),(63,2,20,'XXXXX'),(63,3,10,'XXXXX'),(63,4,30,'XXXXX')
;
CREATE TABLE ClientVisit ( clientvisit_id int, client_id int, non_billable bit, duration int, emp_id int , rev_timein date, splitprimary_clientvisit_id int, gcode_primary_clientvisit_id int ) ;
INSERT INTO ClientVisit ( clientvisit_id, client_id, non_billable, duration, emp_id, rev_timein, splitprimary_clientvisit_id, gcode_primary_clientvisit_id )
VALUES
(1, 1234, 0, 110, 1, getDate(), null, null )
, (2, 1234, null, 120, 1, getDate(), null, null )
, (3, 1234, 1, 110, 2, getDate(), null, null )
, (4, 1234, 0, 130, 2, getDate(), null, null )
;
MAIN QUERY:
; WITH creds AS (
SELECT c.credential_id, c.credentials, r.credential_rank
FROM Credentials c
LEFT OUTER JOIN (VALUES (1,3),(2,2),(3,1) ) r(credential_id, credential_rank)
ON c.credential_id = r.credential_id
)
SELECT DISTINCT
SUM(CASE WHEN ISNULL(v.non_billable,1) = 0 THEN v.duration ELSE 0 END)*1.0 / 60 AS billable_hours,
SUM(CASE WHEN (ISNULL(v.non_billable,1) = 0 AND zsl.payer_id = 63) THEN v.duration ELSE 0 END)*1.0 / 60 AS billable_mro_hours,
s2.credentials
FROM Z_ServiceLedger zsl
INNER JOIN ClientVisit v ON zsl.clientvisit_id = v.clientvisit_id
AND v.rev_timein >= #param1
AND v.rev_timein < DateAdd(d, 1, #param2)
AND v.splitprimary_clientvisit_id IS NULL
AND v.gcode_primary_clientvisit_id IS NULL
AND ISNULL(v.non_billable,1) = 0
AND v.duration*1.0 / 60 > 0
AND v.client_id <> 331771
INNER JOIN (
SELECT s1.emp_id, s1.emp_name, s1.credential_id, s1.credentials, s1.endDate
FROM (
SELECT e.emp_id, e.emp_name, c.credential_id, c.credentials, ISNULL(ec.end_date,GETDATE()) AS endDate
, ROW_NUMBER() OVER (PARTITION BY e.emp_id ORDER BY c.credential_rank) AS rn
FROM Employees e
LEFT OUTER JOIN EmployeeCredential ec ON e.emp_id = ec.emp_id
AND ISNULL(ec.is_primary,1) <> 0 /* I don't think a NULL is_primary should be TRUE */
LEFT OUTER JOIN creds c ON ec.credential_id = c.credential_id
) s1
WHERE s1.rn = 1
) s2 ON v.emp_id = s2.emp_id
AND v.rev_timein <= s2.endDate /* Credential not expired at rev_timein */
WHERE zsl.amount > 0
AND zsl.action_type NOT IN ('SERVICE RATE CHANGE', 'CLIENT STATEMENT')
GROUP BY s2.credentials
ORDER BY s2.credentials
Results:
| billable_hours | billable_mro_hours | credentials |
|----------------|--------------------|-------------|
| 1.833333 | 1.833333 | MA |
| 2.166666 | 2.166666 | PhD |
A couple of things to watch for:
1) Integer Division : duration/60 will return an integer. So if you had duration=70, then you'd have 70/60 = 1. You'd miss that 10 minutes, because of the result will be converted back to an integer. You lose that extra 10 minutes. Probably not what you inteded. The easiest solution is to just multiply duration by 1.0 so that it is forced into a decimal datatype and won't cause the operation to be treated like integers.
2) EmployeeCredential.is_primary != 'False' : Rather than account for the strings of "True"/"False", you should use an actual boolean value (1/0). And a NULL value should indicate that the value is NOT TRUE or FALSE rather than implying TRUE. Also, in SQL, != will work to indicate NOT EQUAL TO, but you should use <> instead. It means the same thing, but is grammatically more correct for SQL.
3) v.non_billable = 0 AND v.non_billable = 'FALSE' : This can be shortened to ISNULL(v.non_billable,1)=0 to short-circuit both checks, especially since non_billable can be NULL. You also avoid the implicit type converstion when comparing the number 0 and the string 'False'.
4) v.client_id != '331771 ' : Change to v.client_id<>33171. First, the != to <> that I mentioned earlier. Then '331771' is implicitly converted to a number. You should avoid implicit conversions.
5) You originally had v.non_billable in your GROUP BY. Since you aren't including it in your SELECT, you can't use it to GROUP BY. Also, you're already filtering out everything other than non_billable=0, so you'd never have more than one value to GROUP BY anyway. Just exclude it.
6) CASE WHEN EmployeeCredential.end_date IS NOT NULL THEN EmployeeCredential.end_date ELSE GETDATE() END : This is the same as saying ISNULL(EmployeeCredential.end_date,GETDATE()).
7) Unless you actually need to filter out specific records for a specific reason, more your JOIN conditions into the JOIN rather than using them in the WHERE clause. This will help you be more efficient with the data your initial query returns before it is filtered or reduced. Also, when using a WHERE filter with a LEFT JOIN, you may end up with unexpected results.

Extracting rows from fact table which have missing data, using metadata table

I have a situation where I have:
A fact table with an id column which is NOT unique but is never null. This fact also has a lot of other dimensions (columns) which may be with a default value -1 (which logically means null)
Example:
id | Dimension1 | Dimension2 | Dimension3
1 Value -1 Value
1 -1 -1 Value
2 -1 Value Value
A metadata table that has the same dimensions as the fact table. Each row in this table represents an unique id from the fact table. Rest of the columns are populated with either null or 1, where 1 means that this dimension is a required dimension in the fact table for this id.
Example:
id | Dimension1 | Dimension2 | Dimension3
1 1 1
2 1 1
My goal is to get ONLY the rows from the fact table that are missing required information according to the metadata table. So from the examples above I would get only the row with id = 1 where Dimension1 = -1, since metadata table says for id = 1 dimensions 1 and 3 are required.
Is there an easy way of doing this?
I have made a very complicated query where there is join between these two tables and a case checks between all dimensions (I have more than 100 of them). Then these checks assign a -1 if dimension is missing in fact but is required, and there is an outer query that would sum these for all rows and only pick up rows with negative sum.
It does not work to 100% and I think its way too complicated to run on a real big fact table, so I'm open to ideas.
edit: Dynamic SQL is not allowed :(
I would suggest using a cte and an except query... in this solution, you will have to add the cases as well, but the join seems far more simple to me and you don't need to sum up any dummy values...
DECLARE #t TABLE(
id int, Dimension1 int, Dimension2 int, Dimension3 int
)
DECLARE #tMeta TABLE(
id int, Dimension1 int, Dimension2 int, Dimension3 int
)
INSERT INTO #t VALUES (1, 123, -1, 345), (1, -1, -1, 246), (2, -1, 567, 987)
INSERT INTO #tMeta VALUES (1, 1, NULL, 1), (2, NULL, 1, 1)
;WITH cte AS(
SELECT id,
CASE WHEN Dimension1 = -1 THEN NULL ELSE 1 END Dimension1,
CASE WHEN Dimension2 = -1 THEN NULL ELSE 1 END Dimension2,
CASE WHEN Dimension3 = -1 THEN NULL ELSE 1 END Dimension3
FROM #t
EXCEPT
SELECT *
FROM #tMeta
EXCEPT
SELECT id, ISNULL(Dimension1,1), ISNULL(Dimension2,1), ISNULL(Dimension3,1)
FROM #tMeta
)
SELECT t.*
FROM #t t
JOIN cte c ON t.id = c.id
AND CASE WHEN t.Dimension1 = -1 THEN -1 ELSE 1 END = ISNULL(c.Dimension1, -1)
AND CASE WHEN t.Dimension2 = -1 THEN -1 ELSE 1 END = ISNULL(c.Dimension2, -1)
AND CASE WHEN t.Dimension3 = -1 THEN -1 ELSE 1 END = ISNULL(c.Dimension3, -1)
You can use UNPIVOT to simplify query also you don't have ROWId in your fact table so the first CTE to make ROW_NUMBER() works as a RowId in the fact table. Then we make unpivoted tables (fact and template table) and join them:
WITH TFBase AS
(
SELECT TF.*, ROW_NUMBER() OVER (ORDER BY ID) as TableRowID FROM TF
),
TFU AS
(
select id,TableRowID,dim,val
from TFBase
unpivot
(
val for dim in (Dimension1, Dimension2, Dimension3)
) u
WHERE U.Val <>-1
)
,
TFT AS
(
select id,dim,val
from TTemplate
unpivot
(
val for dim in (Dimension1, Dimension2, Dimension3)
) u
WHERE Val is NOT NULL
)
SELECT * FROM TFBase WHERE
TableRowID IN
(
SELECT TableRowID FROM TFU
LEFT JOIN TFT ON
(TFU.id=TFT.id) AND (TFU.dim = TFT.dim)
GROUP BY TableRowID, TFU.ID
HAVING COUNT(TFT.Val) <> (SELECT COUNT(*) FROM TFT WHERE ID = TFU.ID)
)

SQL server mismach columns

I'm trying to write a query for SQL Server.
must sum two separate columns
if the column Debe is less than the column Haber should print "mismatch"
The explanation is :
There are two tables ContD and Cont with a common column ID
The table ContD has two important columns, Debe and Haber.
The table Cont has one important column, Importe
If the sum of Debe is not equal to the sum of Haber should report printing a message on another column.
If the final number Debe and Haber are equal, compared to the column Importe of the table Cont and print coincidence in another column
http://www.grupoalta.com/wp-content/uploads/queryconciliacion.png
This should do the trick:
Note: I have commented the script that you actually want to use, the uncommented script is a quick sample that I have made out of the screenshot you shared, but as #Horaciux said, please, include a sample the next time.
Note 2: I supposed that the id in your table Cont is the primary key or it is just Unique, meanwhile the id in the table contD is a foreign key or it just doesn't have the constraint of Uniqueness.
DECLARE #t1 AS TABLE (id NUMERIC,debe DECIMAL(18,2),haber DECIMAL(18,2))
DECLARE #t2 AS TABLE (id NUMERIC,importe DECIMAL(18,2))
INSERT INTO #t1
SELECT 10887,NULL,603.2 UNION ALL
SELECT 10887,83.2,NULL UNION ALL
SELECT 10887,520,NULL UNION ALL
SELECT 10888,NULL,21.344 UNION ALL
SELECT 10888,18.40,NULL UNION ALL
SELECT 10888,2.944,NULL
INSERT INTO #t2
SELECT 10887,603.2 UNION ALL
SELECT 10888,150
SELECT id
,SUM(debe) 'Debe'
,SUM(haber) 'Haber'
,(SELECT importe FROM #t2 where id=t1.id) 'Importe'
,CASE WHEN SUM(debe)=SUM(haber) THEN '' ELSE 'not equal' END 'Debe=Haber'
,CASE WHEN (SUM(debe)=SUM(haber) AND (SELECT importe FROM #t2 WHERE id=t1.id)=SUM(debe)) THEN 'Coincidence' ELSE '' END 'Debe=Haber=Importe'
FROM #t1 t1
GROUP BY id
/*
SELECT id
,SUM(debe) 'Debe'
,SUM(haber) 'Haber'
,(SELECT importe FROM #t2 where id=t1.id) 'Importe'
,CASE WHEN SUM(debe)<>SUM(haber) THEN 'not equal' ELSE '' END 'Debe=Haber'
,CASE WHEN (SUM(debe)=SUM(haber) AND (SELECT importe FROM cont WHERE id=t1.id)=SUM(debe)) THEN 'Coincidence' ELSE '' END 'Debe=Haber=Importe'
FROM contd t1
GROUP BY id
*/
here is an approach that does the comparison and returns one value, depending on the nature of the match. This uses a case statement:
select cd.id,
(case when cd.sumdebe <> cd.sumhaber then 'not equals'
when cd.sumdebe = c.importe then 'all same'
else 'mismatch'
end)
from (select id, sum(debe) as sumdebe, sum(haber) as sumhaber
from contd
group by id
) cd left outer join
cont c
on cd.id = c.id;