Sum With Inner Join Group by Clause - sql

I have two tables, Master and Child. I need the sum of column Cash in the Master table and also I am referring the Child table to match certain conditions.
Master Table:
ID CASH BillDate
1 100 22-02-2014
2 200 22-02-2014
Child Table:
ChildID MasterID
1 1
2 1
3 2
My Query:
select CONVERT(varchar,BillDate,103) as BillDate,SUM(cash)as ByCash
from childdetails CD
inner join MasterDetails MD on MD.ID=CD.MasterID
where CONVERT(varchar,BillDate,103)='22/02/2014'
group by BillDate
My Incorrect Output:
BillDate ByCash
22/02/2014 400
The correct output should be 300 in ByCash, but I'm not sure why its being calculated to be 400.

The issue appears to be that your Child Table counts the MasterID twice. Try selecting the Child table as a CTE with a row_number, partitioned by MasterID to filter out duplicates:
select
CONVERT(varchar,BillDate,103) as BillDate,
SUM(cash)as ByCash
from (
select *,
row_number() over(partition by MasterID order by ChildID) dedupe
from childdetails
) CD inner join MasterDetails MD on MD.ID=CD.MasterID
where
CONVERT(varchar,BillDate,103)='22/02/2014'
and CD.dedupe = 1
group by BillDate

Maybe this is what you're looking for:
SELECT BillDate, SUM(Cash) FROM MasterDetails GROUP BY BillDate
If it isn't, please clarify exactly what your expected output is.

Related

using a select to find info from 2 tables that have similar columns

I have 2 tables very similar, they both partially have same column's name (and datatype), so instead of having to select tables 1 by 1, I wanted to make it so the first table's column become same like second's table column (so like if they have 4 columns with same name, instead of having 8 column after selecting, it shows only 3)
JOIN items i ON i.characterId=c.characterId
WHERE i.itemId=18011
SELECT c.accountId,c.characterId,c.name,b.itemId,b.maxUpgrade,b.amount FROM characters c
JOIN bankItems b ON b.accountId=c.accountId
WHERE b.itemId=18011
here is an example of request I do to select a same info from both tables, I need to do 2 different request and I wish I could fusion them
table 1 (characters):
characterId accountId name
table 2 (items):
characterId itemId maxUpgrade amount
table 3 (bankItems) :
accountId itemId maxUpgrade amount
And in result :
accountId characterId name itemId maxUpgrade amount
but all in 1 request, so no need to type the WHERE c.name= twice
You could do a union of items and bankItems tables within a CTE and then join the characters table on the CTE for example with either the accountId or characterId:
;WITH CTE AS (
SELECT itemId, NULL AS characterId, accountId, maxUpgrade, amount
FROM bankItems
UNION
SELECT itemId, characterId, NULL AS accountId, maxUpgrade, amount
FROM items
)
SELECT
c.accountId,
c.characterId,
c.name,
b.itemId,
b.maxUpgrade,
b.amount
FROM characters c
JOIN CTE b ON
b.accountId = c.accountId
OR b.characterId = c.characterId
WHERE b.itemId = 18011;
Considering the table structure this solution with optional fields should work.

SQL Server 2008 select query difficulty

I have a table with over 100k records. Here my issue, I have a bunch of columns
CompanyID CompanyName CompanyServiceID ServiceTypeID Active
----------------------------------------------------------------
1 Xerox 17 33 Yes
2 Microsoft 19 39 Yes
3 Oracle 22 54 Yes
2 Microsoft 19 36 Yes
So here's how my table looks, it has about 30 other columns but they are irrelevant for this question.
Here's my quandary..I'm trying to select all records where CompanyID and CompanyServiceID are the same, so basically as you can see in the table above, I have Microsoft that appears twice in the table, and has the same CompanyID and CompanyServiceID, but different ServiceTypeID.
I need to be able to search all records where there are duplicates. The person maintaining this data was very messy and did not update some of the columns properly so I have to go through all the records and find where there are records that have the same CompanyID and CompanyServiceID.
Is there a generic query that would be able to do that?
None of these columns are my primary key, I have a column with record number that increments by 1.
You can try something like this:
SELECT CompanyName, COUNT(CompanyServiceID)
FROM //table name here
GROUP BY CompanyName
HAVING ( COUNT(CompanyServiceID) > 1 )
This will return a grouped list of all companies with multiple entries. You can modify what columns you want in the SELECT statement if you need other info from the record as well.
Here's one option using row_number to create the groupings of duplicated data:
select *
from (
select *,
row_number () over (partition by companyId, companyserviceid
order by servicetypeid) rn
from yourtable
) t
where rn > 1
Another option GROUP BY, HAVING and INNER JOIN
SELECT
*
FROM
Tbl A INNER JOIN
(
SELECT
CompanyID,
CompanyServiceID
FROM
Tbl
GROUP BY
CompanyID,
CompanyServiceID
HAVING COUNT(1) > 1
) B ON A.CompanyID = B.CompanyID AND
A.CompanyServiceID = B.CompanyServiceID
Using Join..
Select *
from
Yourtable t1
join
(
select companyid,companyserviceid,count(*)
from
Yourtable
having count(*)>1)b
on b.companyid=t1.companyid
and b.companyserviceid=t1.companyserviceid

delete overflow record per ID

i need to have 4 records or less per ID. some ID's have more than 4 records. I need to delete the records above the limit so i have 4 records per ID.
i tried many things but the only solution i found is just deleting all the records when the ID has more than 4. this is my code for getting the ammount of records per ID:
select count(voorwerpnummer) AS plaatjes, voorwerpnummer
from Illustraties INNER JOIN items
ON Illustraties.itemID = items.ID
INNER JOIN tbl_voorwerp
ON items.ID = tbl_voorwerp.voorwerpnummer
group by voorwerpnummer
order by plaatjes DESC
i have this line to delete the extra records per itemID:
DELETE FROM illustraties
WHERE plaatjefile NOT IN (select top 4 plaatjefile from illustraties where itemID = 110769395358)
AND itemID = 110769395358
now i need to itterate through all the itemID's which have more than 4 records.
this is how to get all the itemID's with more than 4 records:
WITH cte AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY itemID ORDER BY itemID) AS rn
FROM illustraties
)
SELECT distinct ItemID
FROM cte
WHERE rn > 4
can anyone make me a function or something to go through all those itemID's and execute that delete statement?
OR make a query which adds a rownumber per ID.
for example : an ID has 5 records. the 5 records get the numbers 1 to 5. the next ID has 8 records. the 8 records get the numbers 1 to 8.
this way i can delete the records which have an rownumber of 5 or higher.
It is little known that you can delete from a CTE or derived table:
WITH cte AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY itemID ORDER BY itemID) AS rn
FROM illustraties
)
DELETE cte
FROM cte
WHERE rn > 4
I just changed one line. Your ORDER BY should really create a total order. If not SQL Server has some freedom which records to delete. Might be arbitrary and not in your interest.
Hope you need to delete records from Illustraties table.Is that possible for you to share the table structure along with Unique key fields for that table.

Fetch Max from a date column grouped by a particular field

I have a table similar to this:
LogId RefId Entered
==================================
1 1 2010-12-01
2 1 2010-12-04
3 2 2010-12-01
4 2 2010-12-06
5 3 2010-12-01
6 1 2010-12-10
7 3 2010-12-05
8 4 2010-12-01
Here, LogId is unique; For each RefId, there are multiple entries with timestamp. What I want to extract is LogId for each latest RefId.
I tried solutions from this link:http://stackoverflow.com/questions/121387/sql-fetch-the-row-which-has-the-max-value-for-a-column. But, it returns multiple rows with same RefId. The LogId as well as RefId should be unique.
Can someone help me with this?
Thanks
Vamyip
You need to use a subquery that extracts the latest Entered value for each RefId, and then join your source table to this on RefId, Entered:
SELECT DISTINCT MyTable.LogId, MyTable.Entered FROM MyTable
INNER JOIN (SELECT RefId, MAX(Entered) as Entered FROM MyTable GROUP BY RefId) Latest
ON MyTable.RefId = Latest.RefId AND MyTable.Entered = Latest.Entered
Since it appears auto-increment log ID, they would be date/time stamped in sequential order. So, by grabbing the last LogID per Reference ID, you'll have the "most recent" one in the "PreQuery" below, then join based on that single ID to the original table to get the actual date stamp info (or other details) you need from the actual log.
select PreQuery.RefID,
PreQuery.LastLogEntry,
L.Entered
from
( select RefID,
Max( LogID ) LastLogEntry
from
YourLog
group by
RefID ) PreQuery,
YourLog L
where
PreQuery.LastLogEntry = L.LogID
To handle the duplicates correctly:
SELECT m.*
FROM (
SELECT DISTINCT refid
FROM mytable
) md
JOIN mytable m
ON m.LogID =
(
SELECT LogID
FROM mytable mi
WHERE mi.refid = md.refid
ORDER BY
mi.refid DESC, mi.entered DESC, mi.logid DESC
LIMIT 1
)
Create an index on mytable (refid, entered, logid) for this to work fast.

Fill in missing values in a SELECT statement

I have a table with two columns, customer id and order.
Let's say I have in total order IDs 1,2,3,4
All the customer can have all the four orders, like below:
1234 1
1234 2
1234 3
1234 4
3245 3
3245 4
5436 2
5436 4
You can see above that 3245 customer doesn't have order id 1 or 2.
How could I print in the query output like:
3245 1
3245 2
5436 1
5436 3
EDIT: I don't have an order table, but I have a list of order's like we can hard code it in the query(1,2,3,4).
SELECT c.id, o.order
FROM (
SELECT 1 AS order
UNION ALL
SELECT 2 AS order
UNION ALL
SELECT 3 AS order
UNION ALL
SELECT 4 AS order
) o
CROSS JOIN
(
SELECT DISTINCT id
FROM customer_orders
) c
WHERE NOT EXISTS
(
SELECT NULL
FROM customer_orders ci
WHERE ci.id = c.id
AND ci.order = o.order
)
If you have customers table, it becomes more simple:
SELECT c.id, o.order
FROM (
SELECT 1 AS order
UNION ALL
SELECT 2 AS order
UNION ALL
SELECT 3 AS order
UNION ALL
SELECT 4 AS order
) o
CROSS JOIN
customers c
WHERE NOT EXISTS
(
SELECT NULL
FROM customer_orders ci
WHERE ci.id = c.id
AND ci.order = o.order
)
Okay, there are two issues here. The first problem is turning a list of numbers into a rowset. There are a number of different ways of doing this, depending on how you get the numbers into the query. In the following example I use a function which turns a comma-separated string into a nested table, which can be treated as a regular table with the TABLE() function. This is not strictly relevant to the question you pose. If you're interested in this bit of the implementation see my post in this other thread.
The second part of the problem is identifying the missing Orders for each Customer. The obvious approaches - such as using NOT IN with a sub-query - won't work, because the Orders for Customer 1234 match all the Order IDs. We need to do is fill in the missing orders for each Customer. This can be done by using a LEFT OUTER JOIN combined with the PARTITION BY clause. It is then a simple matter to filter out the hits by embedding the LOJ query in an outer SELECT, like this:
SQL> select customer_id
2 , missing_order_id
3 from (
4 select t42.customer_id
5 , t42.order_id
6 , nos.column_value as missing_order_id
7 from ( select * from table (str_to_number_tokens('1,2,3,4'))) nos
8 left outer join t42 partition by ( t42.customer_id )
9 on nos.column_value = t42.order_id
10 )
11 where order_id is null
12 /
CUSTOMER_ID MISSING_ORDER_ID
----------- ----------------
3245 1
3245 2
5436 1
5436 3
SQL>
aside from my comment, and your existing table, I would approach something like this...
select distinct
a.Customer,
b.OrderNumber
from
YourOrderTable a,
( select distinct OrderNumber from YourOrderTable ) b
where
b.OrderNumber NOT IN
( select OrderNumber from
YourOrderTable c
where a.Customer = c.Customer
and b.OrderNumber = c.OrderNumber )
By doing a select distinct as the second table in the FROM clause and no specific join to it, you will get a Cartesian join... ie: for each customer, it will join to every possible order number.
Then, in your WHERE clause, the NOT IN SQL test will only allow the "b." order numbers where none exist in the SQL-subselect (c.)
This could be a very costly query, especially if you have many unique orders..