These are my tables:
Table_Bill_Summary
| Date | PersonId | BillNo |
Table_Bill_Items
| BillNo | ItemId | Quantity |
Item_MasterTable
| ItemId | ItemName | ItemRate |
I am going to specify a PersonId. This is what I want to print:
SELECT *
FROM Table_Bill_Summary
WHERE PersonId=#X
(SELECT IM.ItemName,BI.Quantity,IM.ItemRate
FROM Table_Bill_Items AS BI
INNER JOIN Item_MasterTable AS IM ON BI.ItemId=IM.ItemId
WHERE BI.BillNo=??)
My result should look like:
23/04/2013 Person123 Bill32
Item20 23 100
Item21 20 200
Item23 1 300
23/04/2013 Person123 Bill39
Item2 2 100
Item11 40 800
Item43 1 700
I want a child Select Query to be run for each row retrieved by the parent Select query on the column BillNo
select FLD1,FLD2,FLD3 from
(
SELECT CAST(Date as varchar(10)) as FLD1 ,CAST(PersonId as varchar(10)) as FLD2,
cast(BillNo as varchar(10)) as FLD3,1 as FLD4, Billno as fld5
FROM Table_Bill_Summary WHERE PersonId =#X
union
SELECT CAST(IM.ItemName as varchar(10)) as FLD1 ,CAST(BI.Quantity as varchar(10)) as FLD2,CAST(IM.ItemRate as varchar(10)) as FLD3 ,2 as FLD4, BS.Billno as fld5
FROM Table_Bill_Summary BS inner join Table_Bill_Items AS BI on bs.BillNo=BI.BillNo
INNER JOIN Item_MasterTable AS IM ON BI.ItemId=IM.ItemId
WHERE Bs.PersonId =#X
) as x order by fld5,fld4
You can't do this in SQL. Instead, perform the operation in your application by fetching the joined results (bills and items) sorted by bill and keeping track of the last seen bill when iterating through the resultset:
string stm = #"
SELECT BillNo, Date, ItemName, Quantity, ItemRate
FROM Table_Bill_Summary
JOIN Table_Bill_Items USING (BillNo)
JOIN Item_MasterTable USING (ItemId)
WHERE PersonId = #Person
ORDER BY BillNo
";
MySqlCommand cmd = new MySqlCommand(stm, conn);
cmd.Prepare();
cmd.Parameters.AddWithValue("#Person", person_id);
MySQLDataReader rdr = cmd.ExecuteReader();
bool isMore = rdr.Read();
while (isMore) {
int current_bill = rdr.GetInt32(0);
// output bill
do {
// output item
} while (isMore = rdr.Read() && rdr.GetInt32(0) == current_bill);
}
You can't. (At least not without some queer tricks ;-) An SQL select statement will always result in a list of rows showing the same columns.
You would usually solve this with a reporting software or with another programming language and GUI.
Related
I have the following SQL table:
autID (auto increment, number) | externalID (NOT unique, string) | title | price | scanDate (datetime)
I also have the following query to calculate the average price:
SELECT AVG(price) AS avgPrice from rs
My goal is to calculate the average price of the latest inserted of each autID only (or based on scanDate). For example:
1 | "baba" | 100 | date
2 | "baba | 50| newerDate
3 | "oo | 100| date
will return:
avgPrice: (50 + 100) / 2 = 75
the first 100 is neglected since it's older of the same ID of baba
Any idea?
One method is to use a correlated subquery to get the most recent value and then use average:
select avg(rs.price)
from rs
where rs.scandate = (select max(r2.scandate)
from rs rs2
where rs2.externalid = rs.externalid
) ;
Two steps, first find the newest record for each ID, second, do avg()
WITH CTE123 AS (
Select ID, Title, MAX(Scandate) as Scandate
From avgPrice
GROUP BY ID, Title
)
SELECT avg(rs.price)
FROM avgPrice rs
JOIN CTE123 rp ON rs.id=rp.id and rs.Scandate=rp.Scandate
The first step is to find the ID with its newest scandate, and the second step is to join the first CTE and calculate the average for the results. In this way, the calculation is done based on only the newest records.
I am working on employer's data to find out duplicate employers based on their names.
Data is Like this:
Employer ID | Legal Name | Operating Name
------------- | ---------------| --------------------
1 | AA | AA
2 | BB | AA
3 | CC | BB
4 | DD | DD
5 | ZZ | ZZ
Now if I try to find all duplicates of employer AA the query should return the following result:
Employer ID | Legal Name | Operating Name
------------- | ---------------| --------------------
1 | AA | AA
2 | BB | AA
3 | CC | BB
Employer 1's legal name and Employer 2's Operating Name are the direct match with the search.
But the catch is employer 3 which is not directly related with the search string but employer 2's legal name matches with employer 3's operating name.
And I need the search results up to nth level. I am not sure if that can be achieved by recursive query of something like that.
Please help
I was trying to achieve this by Recursive CTE but then I realized that it is going into infinite recursion. Here is the code:
DECLARE #SearchName VARCHAR(50)
SET #SearchName = 'AA'
;With CTE_EmployerNames
AS
(
-- Anchor Member definition
select *
from [dbo].[Name_Table]
where Leg_Name = #SearchName
OR Op_Name = #SearchName
UNION ALL
-- Recursive Member definition
select N.*
from [dbo].[Name_Table] N
JOIN CTE_EmployerNames C
ON N.ID <> C.ID
AND (N.Leg_Name = C.Leg_Name
OR N.Leg_Name = C.Op_Name
OR N.Op_Name = C.Leg_Name
OR N.Op_Name = C.Op_Name)
)
select *
from CTE_EmployerNames
Update:
I created a stored procedure to achieve what I want. But this procedure is a bit slow because of looping and cursor. As of now this is solving my problem by little compromising with execution time. Any suggestion to optimize it or another way to do this will be highly appreciated. thanks guys. Here is the code:
CREATE PROCEDURE [dbo].[Get_Similar_Name_Employers]
#P_BaseName VARCHAR(100)
AS
BEGIN
DECLARE #ID INT
DECLARE #Leg_Name VARCHAR(50)
DECLARE #Op_Name VARCHAR(50)
-- Create temp table to hold data temporarily
CREATE TABLE #Temp_Employers
(
[ID] [int] NULL,
[Leg_Name] [varchar](50) NULL,
[Op_Name] [varchar](50) NULL,
[Status] [bit] null -- To keep track if that record is processed or not
)
-- Insert all records which are directly matching with search criteria
INSERT INTO #Temp_Employers
SELECT NT.ID, NT.Leg_Name, NT.Op_Name, 0
FROM dbo.Name_Table NT
WHERE NT.Leg_Name = #P_BaseName
OR NT.Op_Name = #P_BaseName
while EXISTS (SELECT 1 from #Temp_Employers where Status = 0) -- until all rows are processed
BEGIN
DECLARE #EmployerCursor CURSOR
SET #EmployerCursor = CURSOR FAST_FORWARD
FOR
SELECT ID, Leg_Name, Op_Name
from #Temp_Employers
where Status = 0
OPEN #EmployerCursor
FETCH NEXT
FROM #EmployerCursor
INTO #ID, #Leg_Name, #Op_Name
WHILE ##FETCH_STATUS = 0
BEGIN
-- For every unprocessed record in temp table check if there is any possible duplicate.
-- and insert all possible duplicate records in same table for further processing to find their possible duplicates
INSERT INTO #Temp_Employers
select ID, Leg_Name, Op_Name, 0
from dbo.Name_Table
WHERE (Leg_Name = #Leg_Name
OR Op_Name = #Op_Name
OR Leg_Name = #Op_Name
OR Op_Name = #Leg_Name)
AND ID NOT IN ( select ID
FROM #Temp_Employers)
-- Update status of recently processed record to avoid processing again
UPDATE #Temp_Employers
SET Status = 1
WHERE ID = #ID
FETCH NEXT
FROM #EmployerCursor
INTO #ID, #Leg_Name, #Op_Name
END
-- close cursor and deallocate memory
CLOSE #EmployerCursor
DEALLOCATE #EmployerCursor
END
select ID,
Leg_Name,
Op_Name
from #Temp_Employers
Order By ID
DROP TABLE #Temp_Employers
END
You are basically trying to build a directed acyclic graph in which the nodes are names and you want to find all the names that lead to your employee.
There is a beginning tutorial at Oracle Tip: Solving directed graph problems with SQL, part 1, and a related StackOverflow question at Directed graph SQL.
You can do this with two self joins. I used DISTINCT to be safe - you don't need it for your example, but probably will for your actual data:
SELECT DISTINCT T2.EMPID, T2.LEGAL_NAME, T.LEGAL_NAME
FROM TABLE T
INNER JOIN TABLE T2 ON T.LEGAL_NAME = T2.OPERATING_NAME
INNER JOIN TABLE T3 ON T2.OPERATING_NAME = T3.OPERATING_NAME
WHERE T.LEGAL_NAME <> T3.LEGAL_NAME
Rename and alias tables and columns as you like.
SQL Fiddle Example
Edit - If you also want records where the op name is simply different from the legal name, UNION those in:
SELECT DISTINCT T2.EMPID, T2.LEGAL_NAME, T.LEGAL_NAME
FROM TABLE T
INNER JOIN TABLE T2 ON T.LEGAL_NAME = T2.OPERATING_NAME
INNER JOIN TABLE T3 ON T2.OPERATING_NAME = T3.OPERATING_NAME
WHERE T.LEGAL_NAME <> T3.LEGAL_NAME
UNION
SELECT EMPID, LEGAL_NAME, OP_NAME
FROM TABLE
WHERE LEGAL_NAME <> OP_NAME
SQL Fiddle Example 2
I have a table similar to the one shown below.
-----------------------------
JOB ID | parameter | result |
-----------------------------
1 | xyz | 10 |
1 | abc | 15 |
2 | xyz | 12 |
2 | abc | 8 |
2 | mno | 20 |
-----------------------------
I want the result as shown below.
parameter | result 1 | result 2 |
----------------------------------
xyz | 10 | 12 |
mno | NULL | 20 |
abc | 15 | 8 |
----------------------------------
My goal is to have a single table which can compare the result values of two different jobs. It can be two or more jobs.
you want to simulate a pivot table since mysql doesn't have pivots.
select
param,
max(case when id = 1 then res else null end) as 'result 1',
max(case when id = 2 then res else null end) as 'result 2'
from table
group by param
SQL FIDDLE TO PLAY WITH
If you are using MySQL there are no "outer join" need to use union right and left join:
Something like:
select t1.parameter, t1.result 'Result 1', t2.result 'Result 2' from
table as t1 left join table as t2
on t1.parameter=t2.parameter
where t1.'JOB ID' = 1 and t2.'JOB ID' = 2
union
select t1.parameter, t1.result 'Result 1', t2.result 'Result 2' from
table as t1 right join table as t2
on t1.parameter=t2.parameter
where t1.'JOB ID' = 1 and t2.'JOB ID' = 2
If the SQL with full outer join will make it more easier:
select t1.parameter, t1.result 'Result 1', t2.result 'Result 2' from
table as t1 outer join table as t2
on t1.parameter=t2.parameter
where t1.'JOB ID' = 1 and t2.'JOB ID' = 2
In Postgres, you can use something like:
select parameter, (array_agg(result))[1], (array_agg(result))[2] from my_table group by parameter;
The idea is: aggregate all the results for a given parameter into an array of results, and then fetch individual elements from those arrays.
I think that you can achieve something similar in MySQL by using GROUP_CONCAT(), although it returns a string instead of an array, so you cannot easily index it. But you can split by commas after that.
select q1.parameter, q2.result as r1, q3.result as r2
from
(select distinct parameter from temp2) q1
left join (select parameter, result from temp2 where job_id = 1) q2
on q1.parameter = q2.parameter
left join (select parameter, result from temp2 where job_id = 2) q3
on q1.parameter = q3.parameter;
It works, but it's not efficient. Still, since I'm gathering you are trying to solve something more complex than what's presented, this might help form your general solution.
While I'm at it, here's a slightly cleaner solution:
select distinct q1.parameter, q2.result as r1, q3.result as r2
from
temp2 q1
left join (select parameter, result from temp2 where job_id = 1) q2
on q1.parameter = q2.parameter
left join (select parameter, result from temp2 where job_id = 2) q3
on q1.parameter = q3.parameter;
I have two tables which I want to combine. The first table is with clients and the other with products. Currently I have 22 products, but I want to have a flexible DB design so instead of having 22 columns in the product DB, I have 1 row for each product for each client so if I add or remove 1 product overall, I don't have to change the DB structure.
I want to have a select statement where I select all products for each client and the output should be in a single row with a column for each product.
I have seen some other questions which are similar, but there the aim is to have all the rows concatenated in 1 column- which I don't want.
Assuming 2 clients and 3 products.
Table client:
ClientId | ClientName
---------------------
1 | Name1
2 | Name2
Table products
ProductId | ClientId | Product
-------------------------------------
1 | 1 | SomeproductA
2 | 1 | SomeproductB
3 | 1 | SomeproductA
4 | 2 | SomeproductC
5 | 2 | SomeproductD
6 | 2 | SomeproductA
The output should be something like:
Table output:
ClientId | ClientName | Product1 | Product 2 | Product 3
-------------------------------------------------------------------
1 | Name1 | SomeproductA | SomeproductB | SomeproductA
2 | Name2 | SomeproductC | SomeproductD | SomeproductA
The perfect solution would also be flexible in the sense that the select statement should count the number of distinct products for each client (they will always be the same for all clients), such that if I add or remove 1 product for all clients, I should not change the select statement.
MYSQL Edition
Here is the query. The joined query generates RowNumber (1,2,3,...) for each product inside each client group using User Defined Variables MySQL feature. The outer query forms a PIVOT table using GROUP BY and CASE with Row Numbers from the inner table. If you need to variable products column count then consider creating this query dynamic adding MAX(CASE WHEN p.RowNum=X THEN p.Product END) as ProductX to the select list.
select Clients.ClientName,
MAX(CASE WHEN p.RowNum=1 THEN p.Product END) as Product1,
MAX(CASE WHEN p.RowNum=2 THEN p.Product END) as Product2,
MAX(CASE WHEN p.RowNum=3 THEN p.Product END) as Product3,
MAX(CASE WHEN p.RowNum=4 THEN p.Product END) as Product4
FROM Clients
JOIN
(
SELECT Products.*,
if(#ClientId<>ClientId,#rn:=0,#rn),
#ClientId:=ClientId,
#rn:=#rn+1 as RowNum
FROM Products, (Select #rn:=0,#ClientId:=0) as t
ORDER BY ClientId,ProductID
) as P
ON Clients.ClientId=p.ClientId
GROUP BY Clients.ClientId
SQLFiddle demo
SQL Server Edition:
select Clients.ClientId,
MAX(Clients.ClientName),
MAX(CASE WHEN p.RowNum=1 THEN p.Product END) as Product1,
MAX(CASE WHEN p.RowNum=2 THEN p.Product END) as Product2,
MAX(CASE WHEN p.RowNum=3 THEN p.Product END) as Product3,
MAX(CASE WHEN p.RowNum=4 THEN p.Product END) as Product4
FROM Clients
JOIN
(
SELECT Products.*,
ROW_NUMBER() OVER (PARTITION BY ClientID ORDER BY ProductID)
as RowNum
FROM Products
) as P
ON Clients.ClientId=p.ClientId
GROUP BY Clients.ClientId
SQLFiddle demo
The answers seem to address both MySQL and SQL Server, so I am adding a further SQL Server Answer here and the logic might work in MySQL too.
Below is a dynamic SQL version in Transact SQL for MS SQL Server.
This enables you to get the same result without having to explicitly write out every column you need in the resultant table as the CASE WHEN solution. The CASE WHEN is nice and simple for a few columns, but I recently had a similar scenario that pivoted to around 200 columns.
For dynamic SQL you essentially compile the query that you want as a string using generated variables and then execute it.
-- variable tables to store data
DECLARE #Clients TABLE(ClientID int,
ClientName nvarchar(10))
DECLARE #Products TABLE(ProductID int,
ClientID int,
Product nvarchar(15))
-- populate the variable tables with sample data
INSERT INTO #Clients
VALUES (1, 'Name1'),
(2, 'Name2')
INSERT INTO #Products
VALUES (1, 1, 'SomeproductA'),
(2, 1, 'SomeproductB'),
(3, 1, 'SomeproductA'),
(4, 2, 'SomeproductC'),
(5, 2, 'SomeproductD'),
(6, 2, 'SomeproductA')
-- display the tables to check
SELECT * FROM #Clients
SELECT * FROM #Products
-- join the two tables and generate a column with rows which will become the new
-- column names (Product_col) which gives a number to each product per client
SELECT c.ClientID,
c.ClientName,
p.ProductID,
p.Product,
CONCAT('Product', ROW_NUMBER()
OVER(PARTITION BY c.ClientID ORDER BY p.Product ASC)) AS Product_col
INTO #Client_Products
FROM #Products p
LEFT JOIN #Clients c ON c.ClientID = p.ClientID
-- view the joined data and future column headings
SELECT * FROM #Client_Products
-- setup for the pivot, declare the variables to contain the column names for pivoted
-- rows and the query string
DECLARE #cols1 AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
-- column name list for products
SET #cols1 = STUFF((SELECT distinct ',' + QUOTENAME(Product_col)
FROM #Client_Products
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SELECT #cols1 -- view the future column names
-- generate query variable string
-- the top select is all the columns you want to actually see as the result
-- The inner query needs the columns you want to see in the result, and the columns
-- you are pivoting with. The pivot needs to select the value you want to go into the
-- new columns (MAX()) and the values that will become the column names (FOR x IN())
SET #query = 'SELECT ClientID,
ClientName,'
+ #cols1 +'
FROM
(
SELECT ClientID,
ClientName,
Product_col,
Product
FROM #Client_Products
) x
PIVOT
(
MAX(Product)
FOR Product_col IN (' + #cols1 + ')
) p'
EXECUTE(#query) -- execute the dynamic sql
DROP TABLE #Client_Products
this is a huge problem i've to solve in sql but i don't know how.
This is my dataset:
customer; publisher; qty
This is a data sample:
CustA; PublX; 10
CustA; PublZ; 20
CustA; PublF; 30
CustB; PublX; 8
CustC; PublD; 9
CustD; PublX; 9
CustD; MyPub; 18
CustE; PublZ; 3
CustE; MyPub; 8
I need to do a Query that get ONLY Customer without "MyPubl" as publisher.
Obviously i can't do :
SELECT * from myTable where Publisher <>"MyPubl"
One solution can be that i create a subset table that aggregate customer in one row like this:
CustA; PublX PublZ PublF; 60
CustB; PublX; 8
etc...
Then with a INSTR i check if MyPub exists in second field ...
This solution my work.. so i ask you How can i do this in SQL (aggregate 'same' customers in one row) ?
Any others suggestion (maybe more elegant) ?
Thanks
You can use NOT IN with a sub query:
SELECT
customer,
publisher,
qty
FROM
books
WHERE
customer NOT IN (
SELECT
DISTINCT customer
FROM
books
WHERE
publisher = 'MyPub'
)
SQL Fiddle Demo
Which will output:
CUSTOMER | PUBLISHER | QTY
---------+-----------+-----
CustA | PublZ | 20
CustA | PublF | 30
CustB | PublX | 8
CustC | PublD | 9
Maybe this:
SELECT * FROM myTable
WHERE customer NOT IN (SELECT customer FROM myTable WHERE Publisher = "MyPubl")
Or if you just want the customers
SELECT DISTINCT customer FROM myTable
Or old skool...
SELECT DISTINCT x.customer
FROM my_table x
LEFT
JOIN my_table y
ON y.customer = x.customer
AND y.publisher = 'MyPub'
WHERE y.customer IS NULL;