Sql Update Count Details after grouping - sql

I have multiple tables in my SQL Server database.
I have one table say Table A which has fields like dispatch,filename,etc.
The second table say Table B has filedetails like filename, dispatchcount, totalcount etc.
There are many other fields in both tables but not relevant to this question.
Requirement is :
I want to update Table B dispatch count after grouping Table A customers where dispatch is Y.
As I want to update the Table B using the result of grouping should I create a temp Table of the result or please guide:
Query:
update Collation_Data set Dqty=T.count1
from (select [collation_code],count(1) as 'count1'
FROM [Tatkal].[dbo].[Tatkal_Merge] T
where Dscan='Y'
group by [collation_code]) where srno=T.[collation_code]

In SQL Server, you can use a join with an aggregation query. I want to point out that you should use left join if you want to update all rows in collation_data, even those with no matches:
update c
set c.Dqty = cm.cnt
from Collation_Data c left join
(select collation_code, count(*) as cnt
from [Tatkal].[dbo].[Tatkal_Merge] m
where Dscan = 'Y'
group by collation_code
) cm
on c.srno = cm.collation_code;
You can also do this with a correlated subquery:
update Collation_Data c
set Dqty = (select count(*)
from [Tatkal].[dbo].[Tatkal_Merge] m
where m.Dscan = 'Y' and m.collation_code = c.collation_code
);
This can be quite efficient with an index on Tatkal_Merge(collation_code, Dscan).

I want to update the Table B using the result of grouping should I create a temp Table of the result or please guide
update c
set c.Dqty=T.count1
from Collation_Data c
join
(select [collation_code],count(1) as 'count1'
FROM [Tatkal].[dbo].[Tatkal_Merge]
where Dscan='Y'
group by [collation_code])t
on c.srno=T.[collation_code]

Related

How can I turn my SELECT INNER JOIN into an UPDATE

I've been trying to turn my SELECT INNER JOIN into an UPDATE.
The query I've made in BigQuery is as follows:
SELECT tt.*
FROM `table` tt
INNER JOIN
(SELECT c_id, MIN(c_orderid) as OrderID, MIN(c_orderdate) AS MinDateTime
FROM `table`
GROUP BY c_id) groupedtt
ON tt.c_orderid = groupedtt.OrderID
AND tt.c_orderdate = groupedtt.MinDateTime
I want to turn this query into an update where it updates c_first_sale and sets it to TRUE.
The query I came up with is:
UPDATE `table` tt
SET tt.c_first_sale = TRUE
FROM `table` as dd
INNER JOIN
(SELECT c_id, MIN(c_orderid) as OrderID, MIN(c_orderdate) AS MinDateTime
FROM `table`
GROUP BY c_id) groupedtt
ON dd.c_orderid = groupedtt.OrderID
AND dd.c_orderdate = groupedtt.MinDateTime
WHERE 1 = 1
But that gives me the following error
UPDATE/MERGE must match at most one source row for each target row
I feel like I'm pretty close, but I'm getting stuck on this.
As mentioned by #Pale, this error occurs when you try to update a table by updating joins with more than one row from the FROM clause as given in this documentation. You can create a separate temporary table, perform a group by using an ID column and export the values into the temporary table. Perform join operation on both the tables using the ID column.

How to change other records based on single records in the query not by updating the table?

I have 3 fiscal_Quarter(FQ) values for each country and I want to replace my 1st and 2nd FQ denominator values with 3rd FQ Denominator value. Below is the sample data which I want to achieve.
Note: I am trying to achieve this in my query result not by update the table data.
This is how my data looks like:
Before
This is what I am trying to achieve.
After
You can update the table by joining itself.
UPDATE a
SET a.Denominator = b.Denominator
FROM tableName a
INNER JOIN tableName b
ON a.Country = b.Country
AND b.FQ = 'FQ3'
Here's a Demo.
Based on your comment, you don't need to update the query but instead show FQ3 Denominator to all FQ.
SELECT a.country, a.fq, a.numerator, b.denominator
FROM TableName a
INNER JOIN tableName b
ON a.Country = b.Country
AND b.fq = 'fq3'
Here's a Demo.
what if you use of window function ?
SELECT *,
max(denominator) over (order by country) denominator1
FROM table
Other option is to use of correlated sub-query
SELECT t.country, t.fq, t.numerATOR,
(SELECT TOP 1 denominator FROM table WHERE country = t.country ORDER BY fq DESC)
FROM table t

How to have a set of combination as a condition in SQL?

I'm working on an integration project and have created a batchlog table where I store which combinations have been exported already and then check new data against that batchlog table. It worked pretty well as long as I mostly just stored one ID in batchlog table, let's say Customer ID and then selected new rows from Customer table like this:
SELECT *
FROM Customer
WHERE CusId NOT IN (SELECT CusID FROM IntegrationBatchlog)
However, now the solution is more complex and same row from Customer table will be exported several times in combination with other data so now I have couple of separate stored procedures and more columns in IntegrationBatchlog table (CusID, OrdertypeID and PaymentMethod) and join clauses in my select so now it's more like.
SELECT * FROM Customer c
JOIN....
JOIN...
JOIN...
WHERE there is not a row with that CusID AND OrderTypeID AND PaymentMethod in batchlog table yet.
So here I should check whether or not this exact combination has already been exported but how do you do that when you have like three several ID columns in batchlog table and you want to exclude those rows where all the three ID's are already present in same row in batchlog table?
One way is to do a LEFT JOIN to the IntegrationBatchLog table and only insert rows that aren't present.
select *
from Customer c
LEFT OUTER JOIN IntegrationBatchLog i
on c.CusId = i.CusId
and c.OrderTypeID = i.OrderTypeID
and c.PaymentMethod = i.PaymentMethod
where
i.CusId is null
Use EXISTS, not IN. This allows multiple column matching
This is standard SQL
SELECT * FROM Customer c
JOIN....
JOIN...
JOIN...
WHERE NOT EXISTS (
SELECT * FROM IntegrationBatchlog I
WHERE C.CusID = I.CusID
AND C.OrderTypeID = I.OrderTypeID
AND C.PaymentMethod = I.PaymentMethod)
SELECT ...
FROM Customer c JOIN ...
WHERE NOT EXISTS (SELECT *
FROM IntegrationBatchLog I
WHERE I.CusID = c.CusId AND
I.OrderTypeId = c.OrderTypeID ...)
Maybe NOT EXISTS would work here. Here's an example from the MySQL docs (I don't know your DB) - http://dev.mysql.com/doc/refman/5.0/en/exists-and-not-exists-subqueries.html
The second example:
"What kind of store is present in no cities?"
SELECT DISTINCT store_type FROM stores
WHERE NOT EXISTS (SELECT * FROM cities_stores
WHERE cities_stores.store_type = stores.store_type);
Maybe yours could be:
SELECT * FROM Customer c
JOIN....
JOIN...
JOIN...
WHERE NOT EXISTS (
SELECT * FROM batchlog WHERE
c.CusID = batchlog.CusID AND
c.OrderTypeID = batchlog.OrderTypeID AND
c.PaymentMethod = batchlog.PaymentMethod
)

Filter a SQL Server table dynamically using multiple joins

I am trying to filter a single table (master) by the values in multiple other tables (filter1, filter2, filter3 ... filterN) using only joins.
I want the following rules to apply:
(A) If one or more rows exist in a filter table, then include only those rows from the master that match the values in the filter table.
(B) If no rows exist in a filter table, then ignore it and return all the rows from the master table.
(C) This solution should work for N filter tables in combination.
(D) Static SQL using JOIN syntax only, no Dynamic SQL.
I'm really trying to get rid of dynamic SQL wherever possible, and this is one of those places I truly think it's possible, but just can't quite figure it out. Note: I have solved this using Dynamic SQL already, and it was fairly easy, but not particularly efficient or elegant.
What I have tried:
Various INNER JOINS between master and filter tables - works for (A) but fails on (B) because the join removes all records from the master (left) side when the filter (right) side has no rows.
LEFT JOINS - Always returns all records from the master (left) side. This fails (A) when some filter tables have records and some do not.
What I really need:
It seems like what I need is to be able to INNER JOIN on each filter table that has 1 or more rows and LEFT JOIN (or not JOIN at all) on each filter table that is empty.
My question: How would I accomplish this without resorting to Dynamic SQL?
In SQL Server 2005+ you could try this:
WITH
filter1 AS (
SELECT DISTINCT
m.ID,
HasMatched = CASE WHEN f.ID IS NULL THEN 0 ELSE 1 END,
AllHasMatched = MAX(CASE WHEN f.ID IS NULL THEN 0 ELSE 1 END) OVER ()
FROM masterdata m
LEFT JOIN filtertable1 f ON join_condition
),
filter2 AS (
SELECT DISTINCT
m.ID,
HasMatched = CASE WHEN f.ID IS NULL THEN 0 ELSE 1 END,
AllHasMatched = MAX(CASE WHEN f.ID IS NULL THEN 0 ELSE 1 END) OVER ()
FROM masterdata m
LEFT JOIN filtertable2 f ON join_condition
),
…
SELECT m.*
FROM masterdata m
INNER JOIN filter1 f1 ON m.ID = f1.ID AND f1.HasMatched = f1.AllHasMatched
INNER JOIN filter2 f2 ON m.ID = f2.ID AND f2.HasMatched = f2.AllHasMatched
…
My understanding is, filter tables without any matches simply must not affect the resulting set. The output should only consist of those masterdata rows that have matched all the filters where matches have taken place.
SELECT *
FROM master_table mt
WHERE (0 = (select count(*) from filter_table_1)
OR mt.id IN (select id from filter_table_1)
AND (0 = (select count(*) from filter_table_2)
OR mt.id IN (select id from filter_table_2)
AND (0 = (select count(*) from filter_table_3)
OR mt.id IN (select id from filter_table_3)
Be warned that this could be inefficient in practice. Unless you have a specific reason to kill your existing, working, solution, I would keep it.
Do inner join to get results for (A) only and do left join to get results for (B) only (you will have to put something like this in the where clause: filterN.column is null) combine results from inner join and left join with UNION.
Left Outer Join - gives you the MISSING entries in master table ....
SELECT * FROM MASTER M
INNER JOIN APPRENTICE A ON A.PK = M.PK
LEFT OUTER JOIN FOREIGN F ON F.FK = M.PK
If FOREIGN has keys that is not a part of MASTER you will have "null columns" where the slots are missing
I think that is what you looking for ...
Mike
First off, it is impossible to have "N number of Joins" or "N number of filters" without resorting to dynamic SQL. The SQL language was not designed for dynamic determination of the entities against which you are querying.
Second, one way to accomplish what you want (but would be built dynamically) would be something along the lines of:
Select ...
From master
Where Exists (
Select 1
From filter_1
Where filter_1 = master.col1
Union All
Select 1
From ( Select 1 )
Where Not Exists (
Select 1
From filter_1
)
Intersect
Select 1
From filter_2
Where filter_2 = master.col2
Union All
Select 1
From ( Select 1 )
Where Not Exists (
Select 1
From filter_2
)
...
Intersect
Select 1
From filter_N
Where filter_N = master.colN
Union All
Select 1
From ( Select 1 )
Where Not Exists (
Select 1
From filter_N
)
)
I have previously posted a - now deleted - answer based on wrong assumptions on you problems.
But I think you could go for a solution where you split your initial search problem into a matter of constructing the set of ids from the master table, and then select the data joining on that set of ids. Here I naturally assume you have a kind of ID on your master table. The filter tables contains the filter values only. This could then be combined into the statement below, where each SELECT in the eligble subset provides a set of master ids, these are unioned to avoid duplicates and that set of ids are joined to the table with data.
SELECT * FROM tblData INNER JOIN
(
SELECT id FROM tblData td
INNER JOIN fa on fa.a = td.a
UNION
SELECT id FROM tblData td
INNER JOIN fb on fb.b = td.b
UNION
SELECT id FROM tblData td
INNER JOIN fc on fc.c = td.c
) eligible ON eligible.id = tblData.id
The test has been made against the tables and values shown below. These are just an appendix.
CREATE TABLE tblData (id int not null primary key identity(1,1), a varchar(40), b datetime, c int)
CREATE TABLE fa (a varchar(40) not null primary key)
CREATE TABLE fb (b datetime not null primary key)
CREATE TABLE fc (c int not null primary key)
Since you have filter tables, I am assuming that these tables are probably dynamically populated from a front-end. This would mean that you have these tables as #temp_table (or even a materialized table, doesn't matter really) in your script before filtering on the master data table.
Personally, I use the below code bit for filtering dynamically without using dynamic SQL.
SELECT *
FROM [masterdata] [m]
INNER JOIN
[filter_table_1] [f1]
ON
[m].[filter_column_1] = ISNULL(NULLIF([f1].[filter_column_1], ''), [m].[filter_column_1])
As you can see, the code NULLs the JOIN condition if the column value is a blank record in the filter table. However, the gist in this is that you will have to actively populate the column value to blank in case you do not have any filter records on which you want to curtail the total set of the master data. Once you have populated the filter table with a blank, the JOIN condition NULLs in those cases and instead joins on itself with the same column from the master data table. This should work for all the cases you mentioned in your question.
I have found this bit of code to be faster in terms of performance.
Hope this helps. Please let me know in the comments.

SQL query look for exact match using inner join

I have 2 tables in SQL Server 2005 db with structures represented as such:
CAR:
CarID bigint,
CarField bigint,
CarFieldValue varchar(50);
TEMP: CarField bigint, CarFieldValue varchar(50);
Now the TEMP table is actually a table variable containing data collected through a search facility. Based on the data contained in TEMP, I wish to filter out and get all DISTINCT CarID's from the CAR table exactly matching those rows in the TEMP table. A simple Inner Join works well, but I want to only get back the CarID's that match ALL the rows in TEMP exactly. Basically, each row in TEMP is supposed to be denote an AND filter, whereas, with the current inner join query, they are acting more like OR filters. The more rows in TEMP, the less rows I expect showing in my result-set for CAR. I hope Im making sense with this...if not please let me know and I'll try to clarify.
Any ideas on how I can make this work?
Thank u!
You use COUNT, GROUP BY and HAVING to find the cars that have exactly that many mathicng rows as you expect:
select CarID
from CAR c
join TEMP t on c.CarField = t.CarField and c.CarFieldValue = t.CarFieldValue
group by CarID
having COUNT(*) = <the number you expect>;
You can even make <the number you expect> be a scalar subquery like select COUNT(*) from TEMP.
SELECT *
FROM (
SELECT CarID,
COUNT(CarID) NumberMatches
FROM CAR c INNER JOIN
TEMP t ON c.CarField = t.CarField
AND c.CarFieldValue = t.CarFieldValue
GROUP BY CarID
) CarNums
WHERE NumberMatches = (SELECT COUNT(1) FROM TEMP)
Haven't tested this, but I don't think you need a count to do what you want. This query ought to be substantially faster because it avoids a potentially huge number of counts. This query finds all the cars which are missing a value and then filters them out.
select distinct carid from car where carid not in
(
select
carid
from
car c
left outer join temp t on
c.carfield = t.carfield
and c.carfieldvalue = t.carfieldvalue
where
t.carfield is null
)
Hrm...
;WITH FilteredCars
AS
(
SELECT C.CarId
FROM Car C
INNER JOIN Temp Criteria
ON C.CarField = Criteria.CarField
AND C.CarFieldValue = Critera.CarFieldValue
GROUP BY C.CarId
HAVING COUNT(*) = (SELECT COUNT(*) FROM Temp)
)
SELECT *
FROM FilteredCars F
INNER JOIN Car C ON F.CarId = C.CarId
The basic premise is that for ALL criteria to match an INNER JOIN against your temp table must produce as many records as there are within that table. The HAVING clause at the end of the FilteredCars query should widdle the results down to those that match all criteria.